一、缓存击穿
描述:
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
解决方案:
设置热点数据永远不过期。
加互斥锁,互斥锁参考代码如下:
说明:
1)缓存中有数据,直接走上述代码13行后就返回结果了
2)缓存中没有数据,第1个进入的线程,获取锁并从数据库去取数据,没释放锁之前,其他并行进入的线程会等待100ms,再重新去缓存取数据。这样就防止都去数据库重复取数据,重复往缓存中更新数据情况出现。
3)当然这是简化处理,理论上如果能根据key值加锁就更好了,就是线程A从数据库取key1的数据并不妨碍线程B取key2的数据,上面代码明显做不到这点。
下面是自己测试写的代码 如有大牛看出那段代码不合适请留言指出,感谢!
package com.jjn.service;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSONArray;
import com.itmayiedu.common.RedisUtils;
import com.itmayiedu.entity.Users;
import com.itmayiedu.mapper.UserMapper;
@Service
public class UserCacheService {
@Autowired
private RedisUtils redisUtils;
@Autowired
private UserMapper userMapper;
private Lock lock= new ReentrantLock();
public Users getUserTest01(Long id) {
long startTime=System.currentTimeMillis();
String key=getClass().getName()+"-"+Thread.currentThread().getStackTrace()[1].getMethodName()+id;
//1.先查redis
String userJson = redisUtils.getString(key);
Users user=null;
if(userJson==null) {
//redis不存在 查数据库
if(lock.tryLock()) {
//System.out.println("我拿到了锁");
try {
user = userMapper.getUser(id);
System.out.println("查询了数据库");
if(user==null) {
return null;
}
//更新缓存信息
redisUtils.set(key, JSONArray.toJSON(user).toString(), 60*15L);
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}else {
//System.out.println("没有拿到锁,正在重试");
user=getUserTest01(id);
}
}else {
user=JSONArray.parseObject(userJson, Users.class);
}
long endTime=System.currentTimeMillis();
float excTime=(float)(endTime-startTime)/1000;
System.out.println("执行时间:"+excTime+"s");
return user;
}
}