Redis缓存击穿、穿透雪崩
前言
Redis 缓存虽提升程序性能与效率(如数据查询),但也带来数据一致性难题(严格意义无解,高一致性需求场景慎用缓存 ),还存在缓存穿透、缓存击穿、缓存雪崩等典型问题,业界有对应解决方案。
一、缓存穿透 - 查不到
1. 概念
用户查询数据,Redis 缓存未命中,向持久层数据库查询也无该数据,查询失败。大量用户请求均如此时,持久层数据库压力剧增,即缓存穿透。
流程:客户端查询 → 缓存未命中 → 持久层数据库查询 → 均无数据,查询失败。
2. 解决方案
(1)布隆过滤器
布隆过滤器是一种数据结构,将所有可能查询的参数以 hash 形式存储,在控制层先校验,不符合则丢弃,避免对持久层数据库的查询压力。
流程示意:
存储层缓存层BloomFilt客户端存储层缓存层BloomFilt客户端发起查询校验(miss/hit)未命中时查询(offline storage)命中返回(hit)返回结果(return)
(2)缓存空对象
当存储层查询未命中(无对应数据),将返回的空对象缓存,同时设置过期时间。后续访问该数据可从缓存获取,保护后端数据源。
流程:客户端查询 → 缓存未命中 → 存储层查询未命中 → 缓存空对象(cache NULL) → 返回客户端。
但存在问题:
- 缓存空值需更多空间存储键,可能存大量空值键。
- 空值设置过期时间后,缓存层与存储层数据会有时间窗口不一致,影响需强一致性的业务。
流程示意:
存储层缓存客户端存储层缓存客户端查询(miss)查询(miss)返回空值,缓存空对象(cache NULL)返回结果(return)
二、缓存击穿 - 量太大,缓存过期
1. 概念
与缓存穿透区别:缓存击穿是某个 key 为热点,承载大量并发访问,当该 key 失效瞬间,持续大并发穿破缓存,直接请求数据库。如热点 key 过期时,大量并发请求直接访问数据库查新数据、写缓存,致数据库压力骤增。
2. 解决方案
(1)热点数据永不过期
从缓存层面,不设置热点 key 的过期时间,避免过期后并发访问数据库问题。
(2)加互斥锁
用分布式锁,保证每个 key 同一时间仅一个线程查询后端服务,其他线程等待。将高并发压力转移到分布式锁,对分布式锁可靠性要求高。
三、缓存雪崩
1. 概念
某一时间段,缓存集中过期失效,或 Redis 宕机。如电商大促,商品缓存集中设置 1 小时,凌晨过期后,访问请求全落数据库,致数据库周期性压力波峰,甚至存储层宕机。
更致命情况:缓存服务器某节点宕机或断网,因自然形成的缓存雪崩虽集中创建缓存,数据库一般能承压(周期性压力);但缓存节点宕机,对数据库压力不可预估,可能瞬间压垮。
流程:客户端请求 → 缓存层失效(过期或宕机) → 所有请求到存储层 → 存储层压力暴增,可能宕机。
2. 解决方案
(1)Redis 高可用
增设多台 Redis,搭建缓存服务器集群(异地多活 )。一台挂掉,其他继续提供服务,避免单点故障致缓存整体失效。
(2)限流降级
缓存失效后,通过加锁或队列控制读写数据库的线程数量。如对某 key,仅允许一个线程查询数据、写缓存,其他线程等待,减轻数据库瞬时压力。
(3)数据预热
正式部署前,预先访问可能大量访问的数据,加载到缓存,设置不同过期时间,让缓存失效时间点尽量均匀,避免集中过期。