缓存处理流程
缓存穿透
缓存穿透,是指查询一个数据库一定不存在的数据。一般情况下redis不会有缓存,就会去访问数据库,大量访问造成数据库压力大从而崩溃。
解决方案:1、对ID的正确性进行校验,不正常的ID无法进入后台。
2、对于不存在的数据缓存为null,超时时间设置短一点。
缓存击穿
当我们缓存key设置过期时间,恰巧在这一刻这个key在某一刻被高并发的访问,把所有的请求都打到了DB中这就可能会导致DB挂了。
解决方案:
使用同步方式锁,单机用synchronized,lock可重入锁等,分布式则用redis的setnx
String getValue(String key) {
String value = redis.get(key);
if (value == null) {
String lockKey = "lock_" + key;
if (redis.setnx(lockKey, "1")) {
//设置超时时间
redis.expire(lockKey, 60);
//数据库取值
value = db.get(key);
redis.set(key, value);
redis.del(lockKey);
} else {
//如果有其他线程在操作,就等候50ms再次尝试获取
Thread.sleep(50);
get(key);
}
}
return value;
}
能保证一致性,但是代码复杂度增大,存在死锁的风险
采用缓存物理不过期,逻辑过期方式,在key对应的value设置一个timeout字段。如果检测到存的时间超过过期时间则异步更新缓存。
String getValue(final String key) {
T t = redis.get(key);
String value = t.getValue();
long timeout = t.getTimeout();
// 当逻辑的超时时间到了时,异步构建缓存
if (timeout <= System.currentTimeMillis()) {
threadPool.execute(new Runnable() {
public void run() {
String lockKey = "lock_"+key;
if (redis.setnx(lockKey, "1")) {
redis.expire(lockKey, 60);
String dbValue = db.get(key);
redis.set(key, dbValue);
redis.del(lockKey);
}
}
});
}
return value;
}
不会阻塞线程、高性能,但是数据库和缓存可能会存在数据不一致。
缓存雪崩
缓存雪崩是指在某一个时间段,缓存集中过期失效。请求都去访问数据库导致数据库崩溃。
解决方案:
1、不同的数据设置不同的超时时间
2、和缓存击穿一样的解决方式。