缓存穿透
指查询一个数据库中一定不存在的数据,那么缓存和数据库中都不存在。这种查询不存在数据的现象称为缓存穿透。
如根据商品编号查询详情;首先去查询缓存,缓存中自然没有然后去查询数据库,如果对这个key的请求量巨大,会直接穿透缓存直接查询数据库给数据库造成很大的压力,大量穿透请求严重情况下会造成数据库宕机。
解决方案:
1、缓存空值:对查询结果为空的情况也进行缓存,值设置为null,不过缓存时间设置短一些,如60s。如果对该key插入了数据到db之后要清理缓存
2、使用布隆过滤器,将所有可能存在数据的key放在布隆过滤器中,查询缓存中没有数据之后,再使用布隆过滤器进行过滤请求,判断查询的key
是否在布隆过滤器中,如果不在,则直接返回不再查询数据库。
总结:
1)针对空数据的key是有限的,重复率比较高的采用缓存空值的方式。
2)针对大量请求key不存在、请求重复率低的数据,就没必要缓存,采用布隆过滤器直接过滤掉。
缓存击穿
某个key是热点数据,扛着高并发请求集中对这个key进行访问,避免了访问数据库。那么在这个key失效的瞬间,持续的高并发就击穿了缓存直接
请求数据库,对数据库造成很大压力。
解决方案:
1、热点key缓存永远不过期
不设置过期时间
将过期时间存在key的value中,程序判断将要过期时,异步线程对改key进行更新
2、互斥锁,mutex lock 。当缓存失效的时候,线程不是立即去查询数据库,而是通过设置锁的方式占坑,如Redis的SETNX,判断返回值,谁拿
到了锁谁去查询数据库然后设置缓存,没拿到锁的线程重试get方法。
缓存雪崩
在某个时间段,发生大规模缓存失效的情况。
有两种可能:
1、缓存服务宕机
2:缓存中大量的key集中过期失效
缓存服务宕机解决方案:
1、宕机前:避免出现宕机,使用集群缓存,保证缓存服务的高可用
可以使用 主从+哨兵 ,Redis Cluster 来避免 Redis 全盘崩溃的情况。
2、宕机后: Hystrix限流&降级,避免MySQL宕机、尽快恢复缓存集群(重启自动从磁盘中加载数据恢复,需要提前开启Redis持久化机制)
缓存集中失效解决方案:
1、使用互斥锁
2、数据预热,并设置不同的失效时间
通过缓存reload机制,预先去设置或更新缓存,在即将大并发访问前手动在后台触发加载缓存不同的key,然后设置不同的过期时间,让缓存
过期失效的时间点尽量均匀错开
3、二级缓存
两个缓存,a1为原始缓存,a2为备份缓存;a1短期,a2长期;a1过期了再去查询a2
4、缓存永远不过期
互斥锁实现逻辑:
public String get(key) { String value = redis.get(key); if (value == null) { //代表缓存值过期 //设置3min的超时,防止del操作失败的时候,下次缓存过期一直不能load db if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功 value = db.get(key); redis.set(key, value, expire_secs); redis.del(key_mutex); } else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓存值即可 sleep(50); get(key); //重试 } } else { return value; } }
END.