一、缓存穿透
查询不存在的数据,请求会直接到数据库。
解决:
(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删除缓存
这种情况出现概率非常小