缓存雪崩
什么是缓存雪崩
由于
- 设置缓存时,key都采用了相同expire
- 更新策略
- 数据热点
- 缓存服务宕机
等原因,可能导致缓存数据同一时刻大规模不可用,或者都更新
解决方案
- 更新策略在时间上做到比较均匀
- 使用的热数据尽量分散到不同的机器上
- 多台机器做主从复制或者多副本,实现高可用
- 实现熔断限流机制,对系统进行负载能力控制
在原有失效时间基础上增加一个随机值,比如1~5分钟的随机,这样每个缓存的过期时间重复率就会降低,集体失效概率也会大大降低。
缓存穿透
什么是缓存穿透
大量并发查询不存在的KEY,导致都直接将压力透传给数据库
为什么多次透传?不存在一直为空。需要注意让缓存能够区分KEY不存在和查询到一个空值
例如:访问id=-1的数据。可能出现绕过Redis依然频繁访问数据库,称为缓存穿透,多出现在查询为null的情况不被缓存时。
解决方案
- 缓存空值的KEY,这样第一次不存在也会被加载会记录,下次拿到有这个KEY
- Bloom过滤或RoaringBitmap判断Key是否存在
最常见的布隆过滤器,将多有可能存在的数据哈希到一个足够大的Bitmap中,一个一定不存在的数据会被这个Bitmap拦截掉,从而避免了对底层存储系统的查询压力
- 完全以缓存为准,
- 使用 延迟异步加载 的策略2,这样就不会触发更新。
更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过5min。
缓存击穿
击穿是针对某一个Key缓存,而雪崩是很多key
某个Key失效时,正好有大量并发请求访问该Key
通常使用缓存 + 过期时间的策略来帮助我们加速接口的访问速度,减少了后端负载,同时保证功能的更新,一般情况下这个种模式已经基本满足要求了
但如下问题若同时出现,可能对系统致命:
- 为热点key,访问量非常大
- 缓存的构建是需要时间(可能是个复杂过程,例如复杂SQL、多次I/O、多个接口依赖)
于是就会导致: 在缓存失效瞬间,有大量线程构建缓存,导致后端负载加剧,甚至可能让系统崩溃。
解决方案
所有问题就在限制处理线程的数量,即KEY的更新操作添加全局互斥锁。
互斥锁
在缓存失效时(判断拿出来的值为空),不是立即去load db,而是
- 先使用缓存工具的某些带成功操作返回值的操作(Redis的SETNX)去set一个mutex key
- 当操作返回成功时,再load db的操作并回设缓存;否则,就重试整个get缓存的方法。
缓存并发
这里的并发指的是多个redis的client同时set
key引起的并发问题。其实redis自身就是单线程操作,多个client并发操作,按照先到先执行的原则,先到的先执行,其余的阻塞。当然,另外的解决方案是把redis.set操作放在队列中使其串行化,必须的一个一个执行。
缓存预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。
这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决思路:
1、直接写个缓存刷新页面,上线时手工操作下;
2、数据量不大,可以在项目启动的时候自动进行加载;
目的就是在系统上线前,将数据加载到缓存中。
如何预防缓存雪崩
在缓存的时候给过期时间在加上一个随机值,这样就会大幅度减少缓存在同一时间过期
对于“Redis挂掉了,请求全部走数据库”这种情况,我们可以有以下的思路
- 事发前:万一Redis的高可用(采用主从架构),尽量避免Redis挂掉这种情况
- 事发中:万一Redis真的挂了,可以设置本地缓存(ehcache) + 限流(hystrix),尽量避免数据库被干掉
- 事发后:redis持久化,重启后自动从磁盘加载数据,快速恢复缓存数据
缓存的高可用性
缓存层设计成高可用,防止缓存大面积故障。即使个别节点,个别机器,甚至是机房宕机,依然可以提供服务,例如Redis Sentinel 和 Redis Cluster 都实现了高可用。
缓存降级
可以利用ehcache等本地缓存,但主要还是对源服务访问进行限流、资源隔离、降级等
当访问量剧增、服务出现问题仍然需要保证服务还是可用的。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级,这里会涉及到运维的配合。