# 雪崩

雪崩就是指缓存中大批量热点数据过期后系统涌入大量查询请求,因为大部分数据在 Redis 层已经失效,请求渗透到 DB ,引起数据库压力造成查询堵塞甚至 宕机

举个简单的例子

一个电商平台,如果首页所有的 Key 失效时间都是 12 小时,零点有个限时秒杀活动大量用户杀来,假设当时 6000/s 个请求,本来缓存可以顶住 5000/s 个请求,但是当时所有缓存的 Key 都失效了,这时每秒 6000 个请求全部落在数据库,犹如洪水决堤,造成宕机。

解决方案

  1. 不设置过期时间 (不建议)
  2. 设置不同的过期时间,避免同一时间大量 key 失效。比如利用随机模块生成一个过期时间,这样可以保证数据不会在同一时间大面积失效
  3. 如果缓存数据库是分布式部署,将热点数据均匀分布在不同 Redis 和数据库中,有效分担压力

# 击穿

缓存击穿跟缓存雪崩有些类似,雪崩是大面积缓存失效,导致数据库崩溃,而缓存击穿是一个 Key 是热点,不停地扛住大并发请求,全都集中访问此 Key , 而当此 Key 过期瞬间,持续的大并发就击穿缓存,全都打在 DB 上,就像在完好无损的杯子上凿开一个洞。

解决方案

  1. 把这个热点 key 设置为永久有效
  2. 使用互斥锁,这是比较常用的方法,简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去查询数据库,而是先使用缓存工具的某些带成功操作返回值的操作(比如 Redis 的 SETNX 或者 Memcache 的 ADD)去 set 一个 mutex key,当操作返回成功时,再进行查询数据库的操作并回设缓存

# 穿透

指查询一个数据库一定不存在的数据。正常的使用缓存流程大致是,数据查询先进行缓存查询,如果 key 不存在或者 key 已经过期,再对数据库进行查询,并把查询到的对象,放进缓存。如果数据库查询对象为空,则不放进缓存。

举个简单的例子

比如我传一个用户 id 为 - 1,这个用户 id 在缓存里面是肯定不存在的,所以会去数据库里面查询,如果有搞事情的人,大批量请求并传用户 id 为 - 1,那 redis 就形同虚设,无用武之地,导致数据库压力过大而崩溃。

解决方案

  1. 在接口层增加校验,比如用户鉴权校验,不合法的参数直接返回。不相信任务调用方,根据自己提供的 API 接口规范来,作为被调用方,要考虑可能任何的参数传值。

  2. 布隆过滤器(Bloom Filter), 能很好地防止缓存穿透。原理就是利用高效的数据结构和算法快速判断出你这个 Key 是否在 DB 中存在,从而避免了对底层存储系统的查询压力。

  3. 在缓存查不到, DB 中也没有的情况,可以将对应的 key 的 value 写为 null,或者其他特殊值写入缓存,同时将过期失效时间设置短一点,以免影响正常情况。这样是可以防止反复用同一个 ID 来暴力攻击

# 总结

一般避免以上情况发生从三个时间段去分析:

  • 事前: Redis高可用主从 + 哨兵Redis cluster ,避免全盘崩溃
  • 事中:本地 ehcache缓存 + Hystrix限流 + 降级 ,避免 DB 被打死
  • 事后:Redis 持久化 RDB + AOF ,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据
更新于

请我喝[茶]~( ̄▽ ̄)~*

Daniel ✨ 微信支付

微信支付

Daniel ✨ 支付宝

支付宝