一、缓存穿透
查询不存在的数据,请求会直接到数据库。
解决:
(1)缓存空值
(2)布隆过滤器BloomFilter
二、缓存击穿
热点key突然失效,大量请求到达数据库
解决:
分布式锁
三、缓存雪崩
大部分key失效
解决:
设置逻辑过期时间
错开物理过期时间
public class RedisCache {
Jedis jedis = new Jedis("redis://localhost:6379/9");
DbService db = new DbService();
//redis初始化,bloomFilter加值
//数据库添加值,缓存添加值,bloomFilter加值
//数据库删,缓存删,重构bloomFilter
BloomFilter<CharSequence> bloomFilter= BloomFilter.create(Funnels.stringFunnel(),10000, 0.001);
public String query(String key) {
//缓存穿透
if (!bloomFilter.mightContain(key)) {
return "空数据";
}
//查询缓存
String value = jedis.get(key);
//缓存命中
if (value != null) {
return value;
} else {
//缓存未命中,查询数据库,在更新缓存。
//分布式锁,防止缓存击穿造成数据库大量访问
jedis.set(key, "threadId", "NX", "PX", 10);
//查询缓存
value = jedis.get(key);
if (value != null) {
return value;
}
String dbValue = db.query(key);
if (dbValue != null) {
jedis.set(key, dbValue);
return dbValue;
}
//释放锁
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(key), Collections.singletonList("threadId"));
}
return "";
}
}
四、缓存一致性
以为更新数据库和更新缓存不是原子操作,所以多线程会出现问题。
1.先更新数据库再更新缓存
如果线程A更新数据库 ->线程B更新数据库->线程B更新缓存->线程A更新缓存
会造成数据不一致,先更新缓存再更新数据库同理。
2.先删除缓存再更新数据库
如果线程A删除缓存->线程B查询发现缓存不存在,查询数据库并更新缓存->线程A更新数据库
会造成数据不一致。
3. 先更新数据库再删除缓存
缓存刚好失效 ->线程B查询发现缓存不存在,查询数据库->线程A更新数据库->线程A删除缓存>线程B删除缓存
这种情况出现概率非常小