🌨️ 缓存雪崩
为了保证缓存中数据与数据库中的数据的一致性,通常 Redis 中数据会设置过期时间。如果(1)缓存层在同一时间有大量数据失效,或者 (2)Redis 由于某些情况宕机,导致所有请求全部访问数据库,造成数据库宕机。
💡 解决办法:
🧩 给数据随机设置过期时间
🧩 互斥锁,只有一个请求会到数据库上,并会重建缓存
🧩 双 Key 策略,有不同的 Key,但有相同的 Value
- 主 Key:设置过期时间
- 备 Key:不设置过期时间
- 当主 Key 过期后,大量请求到达时直接返回备 Key,并通知后台线程,重建主 Key
🧩 设置 Key 永不过期,后台更新缓存(内存不够时可能被内存淘汰策略淘汰)
- 后台线程定时更新 或 定时检测是否失效,进行重建
- 业务上发现被淘汰,通过消息队列通知重建
⚡ 缓存击穿
缓存中某个热点 Key 数据过期了,此时有大量请求访问该数据,导这些请求全部都落到了数据库上,导致数据库宕机。
💡 解决办法:
🧩 互斥锁,只有一个请求会到数据库上,并重建缓存
🧩 热点数据永不过期(内存不够时可能被内存淘汰策略淘汰),后台线程更新缓存
🌪️ 缓存穿透
请求的数据既不存在于缓存中,也不存在于数据库中,有大量这样的请求,不会命中缓存,直接访问数据库,导致数据库压力非常大。
可能是由于(1)业务误操作 或 (2)黑客的故意攻击
💡 解决办法:
🧩 缓存默认值,后续的请求直接返回该默认值
🧩 布隆过滤器,当布隆过滤器返回 0 时,说明数据一定不在数据库中
🧩 非法请求限制,在业务层判断查询条件,如果是恶意请求直接拒绝
🔥 热点 Key 重建
当热点缓存失效的瞬间,后端会有大量请求,在重建缓存时,会造成后端负载过大,导致应用崩溃。
💡 使用互斥锁
- 只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,才能从缓存获取数据
💡 Key 永不过期
- 利用后台线程去单独构建缓存
🎏 缓存一致性
更新
❌ 先更新数据库,再更新缓存
❌ 先更新缓存,再更新数据库
删除
❓ 先删除缓存,再更新数据库
可以考虑延迟双删。在更新完数据库后,延迟一段时间再删除一次缓存。
⭕ 先更新数据库,再删除缓存
在实际中,出现的概率非常低。缓存的写入要远远快于数据库的写入。
异步
通过 更新 + 删除,是两个不同的操作,不具备原子性,可能会导致不一致的问题。
- 中间件
通过中间件(如 Canal -> https://github.com/alibaba/canal )订阅数据库的 binlog,当更新完数据库时,通过中间件去删除缓存。
📕 参考
- https://xiaolincoding.com/redis/
- Redis 开发与运维