首页 留言板 后台管理
开源项目:

搜索中...

未找到与 "" 相关的文章

换个关键词试试看

输入关键词搜索文章

支持搜索标题、内容、摘要

Redis 缓存三大问题:穿透、击穿与雪崩及解决方案

admin · 2026-04-29 16:46 · Redis · 2

引言

在高并发场景下,Redis 作为缓存层已经成为标准配置。然而,当缓存与数据库配合不当时,容易出现缓存穿透缓存击穿缓存雪崩三大经典问题。本文将深入剖析这三个问题的成因,并给出切实可行的解决方案。

一、缓存穿透(Cache Penetration)

问题描述

缓存穿透是指查询一个不存在的数据。由于缓存中不会有这个结果,请求会直接打到数据库上。如果有大量这样的请求,数据库压力会急剧上升,甚至导致宕机。

典型场景:恶意攻击者构造大量不存在的 ID 进行查询。

解决方案

1. 布隆过滤器(Bloom Filter)

在缓存之前加一层布隆过滤器,用于快速判断一个元素是否可能存在于集合中。如果布隆过滤器判断不存在,直接返回,避免查询数据库。

// 使用 RedisBloom 模块
BF.ADD redis:user:filter 10086
BF.EXISTS redis:user:filter 10086 // 返回 1
BF.EXISTS redis:user:filter 99999 // 返回 0,直接拦截

2. 缓存空值

当数据库查询结果为空时,也将这个空结果缓存起来,但设置一个较短的过期时间(如 60 秒)。

if ($user === null) {
    Redis::setex("user:{$id}", 60, 'NULL');
}

二、缓存击穿(Cache Breakdown)

问题描述

缓存击穿是指一个热点 key在缓存过期的瞬间,有大量并发请求同时访问这个 key。由于缓存已失效,所有请求都会打到数据库上。

典型场景:微博热搜、电商大促时的爆款商品信息。

解决方案

1. 互斥锁(Mutex)

当缓存失效时,只有一个线程能去查询数据库并重建缓存,其他线程等待。

$lockKey = "lock:{$key}";
$locked = Redis::setnx($lockKey, 1);
if ($locked) {
    Redis::expire($lockKey, 10);
    $data = DB::table('users')->find($id);
    Redis::setex($key, 3600, serialize($data));
    Redis::del($lockKey);
}

2. 逻辑过期(永不过期)

不给热点 key 设置物理过期时间,而是在 value 中存储逻辑过期时间。发现过期时,后台异步重建缓存,前台返回旧数据。

[
    "data" => $user,
    "expire_at" => time() + 3600
]

三、缓存雪崩(Cache Avalanche)

问题描述

缓存雪崩是指在某一个时刻,大量缓存 key 同时过期,或者 Redis 集群整体故障,导致所有请求直接打到数据库上。

典型场景:批量导入数据时设置了相同的过期时间;Redis 节点宕机。

解决方案

1. 过期时间加随机值

在设置缓存过期时间时,加上一个随机值,避免大量 key 同时失效。

$ttl = 3600 + rand(0, 300); // 基础过期时间 + 0~5分钟随机值
Redis::setex($key, $ttl, $value);

2. 多级缓存

构建本地缓存(如 Laravel File/Array Cache)+ Redis 分布式缓存的两级架构。当 Redis 不可用时,本地缓存仍能承接部分流量。

3. Redis 高可用架构

  • 主从复制:读写分离,从节点提供读服务
  • Sentinel 哨兵:自动故障转移,主节点宕机时自动选举新主节点
  • Cluster 集群:数据分片存储,提升整体可用性和容量

4. 服务熔断与限流

当数据库压力过大时,启动熔断机制,直接返回降级数据或友好提示,保护数据库不被拖垮。

四、总结对比

问题成因核心解决思路
缓存穿透查询不存在的数据布隆过滤器、缓存空值
缓存击穿热点 key 过期瞬间并发互斥锁、逻辑过期
缓存雪崩大量 key 同时过期 / Redis 故障随机 TTL、多级缓存、高可用架构

在实际项目中,这三种问题往往同时存在,需要根据业务场景组合使用上述方案。合理的缓存策略不仅能提升系统性能,更是保障系统稳定性的重要防线。

微博 Twitter

评论 (0)

暂无评论,来说两句吧!

发表评论

支持 Markdown 语法和 Emoji 😀