Redis 缓存穿透、击穿与雪崩:原理与解决方案

# Redis 缓存穿透、击穿与雪崩:原理与解决方案 用 Redis 做缓存几乎是后端项目的标配,但缓存用不好,反而会在高并发时把数据库拖垮。缓存穿透、击穿、雪崩是最常被问到、也最容易踩坑的三个问题,这篇文章把它们捋清楚。 ## 1. 缓存穿透:查询一个根本不存在的数据 **现象**:请求查询一个数据库里也不存在的 key,缓存里自然也没有,每次请求都要打到数据库上。如果有人恶意用不存在的 id 疯狂请求,数据库很容易被打垮。 **解决方案**: - **缓存空值**:查询结果为空时,也把这个空结果缓存起来(设置一个较短的过期时间),避免同一个不存在的 key 反复穿透到数据库。 ```python value = redis_client.get(key) if value is None: value = query_from_db(key) if value is None: redis_client.setex(key, 60, '') # 缓存空值,60 秒过期 else: redis_client.setex(key, 3600, value) ``` - **布隆过滤器**:提前把所有合法的 key 放进布隆过滤器,请求先经过过滤器判断,不在里面的 key 直接拒绝,连缓存都不用查。 ## 2. 缓存击穿:一个热点 key 突然过期 **现象**:某个访问量极高的 key(比如首页热门文章)缓存过期的瞬间,大量并发请求同时穿透到数据库,短时间内造成数据库压力骤增。 **解决方案**: - **加互斥锁**:缓存失效时只让一个请求去查数据库并重建缓存,其他请求等待或读旧值。 ```python lock_key = f'lock:{key}' if redis_client.set(lock_key, '1', nx=True, ex=10): try: value = query_from_db(key) redis_client.setex(key, 3600, value) finally: redis_client.delete(lock_key) else: time.sleep(0.05) value = redis_client.get(key) # 重试读取 ``` - **热点数据不过期**:对特别热门的 key,直接不设置过期时间,靠后台任务异步更新。 ## 3. 缓存雪崩:大量 key 集中在同一时刻过期 **现象**:如果给一批 key 设置了相同的过期时间,到了那个时间点,缓存集体失效,请求瞬间全部涌向数据库。 **解决方案**: - **过期时间加随机值**:避免大量 key 在同一时刻集中失效。 ```python import random expire_time = 3600 + random.randint(0, 300) # 基础 1 小时 + 0~5 分钟随机抖动 redis_client.setex(key, expire_time, value) ``` - **多级缓存**:本地缓存(如内存缓存)+ Redis 缓存,即使 Redis 整体不可用,也能兜底一部分请求。 - **限流 + 降级**:数据库压力过大时,通过限流保护数据库,必要时返回降级数据(比如旧数据或默认值)。 ## 小结 | 问题 | 触发场景 | 核心思路 | | --- | --- | --- | | 缓存穿透 | 查询不存在的数据 | 缓存空值 / 布隆过滤器 | | 缓存击穿 | 单个热点 key 过期 | 互斥锁 / 热点不过期 | | 缓存雪崩 | 大量 key 同时过期 | 过期时间加随机 / 多级缓存 | 这三个问题看着相似,但触发条件和解法都不一样,面试也经常放在一起问。理解清楚各自的场景,才能在实际项目里对症下药。
作者: yanleaf 发布时间: 2026-07-03 15:58:23 阅读数: 7 点赞数: 0 评论数:

评论区

备案期间,评论功能暂时关闭。