Redis缓存穿透、击穿、雪崩区别和解决

前言

用Redis做缓存,经常听到三个词:缓存穿透、缓存击穿、缓存雪崩。很多人搞不清区别,本文一次讲明白,附解决方法。

一、三个概念对比

问题 是什么 谁在访问 影响
缓存穿透 查一个不存在的数据,缓存没有,每次都去查数据库 恶意攻击/大量无效请求 数据库压力大
缓存击穿 一个热点key突然过期,大量请求同时打过去 正常用户 数据库瞬间压力大
缓存雪崩 大量key同时过期,或者Redis挂了 正常用户 数据库直接崩

二、缓存穿透

是什么

黑客故意查一个数据库里根本不存在的数据,缓存里也没有,每次请求都打到数据库。

例子: 查用户ID=-1,数据库里没有,缓存里也没有,每次都查数据库。

危害

数据库被大量无效请求打挂。

解决方法

方法1:缓存空值

数据库查不到,也往缓存里写一个空值,设个短过期时间。

// 先查缓存
$user = $redis->get("user:$id");

if ($user === false) {
    // 查数据库
    $user = mysqli_query("SELECT * FROM users WHERE id = $id");

    if (!$user) {
        // 查不到也缓存,设60秒过期
        $redis->setex("user:$id", 60, "null");
    } else {
        $redis->setex("user:$id", 3600, $user);
    }
}

方法2:布隆过滤器

在缓存前面加一层布隆过滤器,快速判断这个ID存在不存在。

不存在的直接返回,不用查数据库。

方法3:接口校验

在接口层就做参数校验,比如用户ID必须大于0,不合法的直接拒绝。

三、缓存击穿

是什么

一个热点key(比如首页热点新闻),突然过期了,这时候刚好有大量用户同时访问,所有请求都打到数据库。

和穿透的区别: 这个key本来是存在的,只是过期了。

危害

数据库瞬间压力暴增。

解决方法

方法1:互斥锁

只让一个线程去查数据库,其他线程等一下。

$lockKey = "lock:user:$id";

// 尝试加锁
if ($redis->setnx($lockKey, 1)) {
    // 加锁成功,查数据库
    $user = mysqli_query("SELECT * FROM users WHERE id = $id");
    $redis->setex("user:$id", 3600, $user);
    $redis->del($lockKey);
} else {
    // 加锁失败,等一下再读缓存
    sleep(100);
    $user = $redis->get("user:$id");
}

方法2:热点key不过期

热点数据不设过期时间,后台定时更新。

方法3:提前预热

在热点数据快过期前,主动后台更新缓存。

四、缓存雪崩

是什么

大量key在同一时间过期,或者Redis整个挂了,所有请求都打到数据库。

和击穿的区别:

  • 击穿:一个key过期
  • 雪崩:大量key同时过期

危害

数据库直接被打挂,整个服务不可用。

解决方法

方法1:过期时间加随机值

不要所有key都设3600秒过期,加个随机数错开。

// 基础3600秒,加0-600秒随机
$expire = 3600 + rand(0, 600);
$redis->setex("user:$id", $expire, $data);

方法2:Redis集群

Redis挂了还有备用的,不要单点。

方法3:服务降级

数据库扛不住的时候,直接返回默认数据或者报错,不要让请求都打过去。

方法4:热点数据永不过期

核心数据不设过期,后台异步更新。

五、总结对比

问题 原因 解决
缓存穿透 查不存在的数据 缓存空值 + 布隆过滤器
缓存击穿 热点key过期 互斥锁 + 热点永不过期
缓存雪崩 大量key同时过期 过期时间加随机 + Redis集群

六、常见坑

坑1:把穿透和击穿搞混

  • 穿透:数据本来就不存在
  • 击穿:数据存在,但缓存过期了

坑2:缓存空值不设过期时间

如果不设过期时间,以后这个数据真的有了,缓存还是空的。

解决: 空值也要设过期时间(比如60秒)。

坑3:互斥锁死锁

加了锁没释放,其他线程一直等。

解决: 锁也要设过期时间。

坑4:随机值加太小

所有key还是差不多同时过期。

建议: 随机范围至少几百秒。

总结

记住:

  1. 穿透:查不存在的数据 → 缓存空值 + 布隆过滤器
  2. 击穿:热点key过期 → 互斥锁 + 热点永不过期
  3. 雪崩:大量key同时过期 → 过期时间加随机 + Redis集群

最简单的防雪崩:

$expire = 3600 + rand(0, 600);
$redis->setex($key, $expire, $data);

遇到问题加QQ23979811 协助处理


emer 发布于  2026-10-9 07:47 

Redis 缓存穿透解决

Redis 缓存穿透解决

缓存穿透是常见问题,恶意请求能把数据库打挂。

1. 什么是缓存穿透

现象: 请求的数据在缓存和数据库中都不存在,每次请求都直接打到数据库。

危害:

  • 恶意攻击时数据库压力大
  • 正常请求被拖慢
  • 数据库CPU飙升

2. 为什么会穿透

  • 查询一个不存在的ID
  • 缓存没有命中
  • 数据库也查不到
  • 请求直接穿透到数据库

3. 解决方案一:缓存空值

思路: 即使数据库查不到,也在Redis存一个空值,设置短过期时间。

function getUser($id) {
    $key = "user:$id";
    $user = $redis->get($key);

    if ($user !== false) {
        // 空值标记
        if ($user === 'null') {
            return null;
        }
        return json_decode($user, true);
    }

    // 查数据库
    $user = $db->query("SELECT * FROM users WHERE id = $id");

    if ($user) {
        // 存在就缓存1小时
        $redis->setex($key, 3600, json_encode($user));
    } else {
        // 不存在也缓存,5分钟过期
        $redis->setex($key, 300, 'null');
    }

    return $user;
}

优点: 简单有效
缺点: 占内存,可能被恶意key刷爆

4. 解决方案二:布隆过滤器

思路: 在缓存前面加一层布隆过滤器,不存在的key直接拦截。

原理:

  • 所有存在的数据先存到布隆过滤器
  • 请求先过布隆过滤器
  • 不存在直接返回,不查缓存和数据库

PHP 示例(用 Redis 的布隆过滤器模块):

# 加载 RedisBloom 模块
redis-cli BF.ADD user:1
redis-cli BF.EXISTS user:1
function getUser($id) {
    $key = "user:$id";

    // 先过布隆过滤器
    if (!$redis->rawCommand('BF.EXISTS', 'user_filter', $id)) {
        return null; // 一定不存在
    }

    // 布隆说可能存在,再查缓存
    $user = $redis->get($key);
    if ($user) {
        return json_decode($user, true);
    }

    // 查数据库...
}

优点: 内存占用小,效率高
缺点: 有误判率,实现复杂

5. 解决方案三:参数校验

思路: 从源头过滤非法请求。

function getUser($id) {
    // 参数校验
    if ($id <= 0 || !is_numeric($id)) {
        return null;
    }

    // 正常查询逻辑...
}

适用: ID类查询,不合法直接拦截

6. 三种方案对比

方案 优点 缺点 适用场景
缓存空值 简单 占内存 数据量不大
布隆过滤器 高效 误判,复杂 数据量大
参数校验 最直接 只防非法 ID类请求

7. 生产建议

1)组合使用

  • 先做参数校验
  • 再缓存空值
  • 大流量用布隆过滤器

2)空值过期时间要短

  • 5分钟到10分钟
  • 不要太长

3)监控异常请求

  • 发现大量404要警惕
  • 及时加防护

4)接口限流

  • 用 Nginx 或网关限流
  • 防止恶意刷

8. 缓存雪崩和击穿

缓存穿透: 查不存在的数据
缓存击穿: 热点key过期,大量请求打数据库
缓存雪崩: 大量key同时过期

区分清楚: 三个问题现象类似但原因不同,解决方案也不同。

总结

Redis 缓存穿透解决要点:

  • 查不存在的数据穿透到数据库
  • 缓存空值是最简单方案
  • 布隆过滤器适合大数据量
  • 参数校验从源头拦截
  • 空值过期时间要短
  • 生产建议组合使用
  • 注意和雪崩、击穿区分开
  • 监控异常请求
  • 接口加限流

缓存穿透是面试高频题,也是生产常见问题,一定要掌握。


emer 发布于  2026-10-9 07:36 

Redis 缓存穿透解决

Redis 缓存穿透解决

缓存穿透是常见问题,大量请求查询不存在的数据,直接打到数据库。这篇讲怎么解决。

1. 什么是缓存穿透

用户查询一个不存在的数据,Redis 里没有,每次都去查数据库。恶意攻击的话,数据库扛不住。

2. 解决方案一:缓存空值

查询数据库也没结果,就缓存一个空值,设置短过期时间。

function getUser($id) {
    $key = "user:$id";

    // 先查缓存
    $user = $redis->get($key);
    if ($user !== false) {
        return json_decode($user, true);
    }

    // 查数据库
    $user = $db->query("SELECT * FROM users WHERE id = $id");

    if ($user) {
        // 缓存有数据的结果
        $redis->setex($key, 3600, json_encode($user));
    } else {
        // 缓存空值,短时间过期
        $redis->setex($key, 60, '');
    }

    return $user;
}

优点: 简单粗暴
缺点: 浪费内存,可能导致不一致

3. 解决方案二:布隆过滤器

在缓存前面加一层布隆过滤器,不存在的 key 直接拦截。

// 使用 RedisBloom 模块
// 添加到布隆过滤器
$redis->rawCommand("BF.ADD", "user_filter", "1001");

// 查询是否存在
$exists = $redis->rawCommand("BF.EXISTS", "user_filter", "1001");
if (!$exists) {
    return null; // 直接返回,不查数据库
}

原理: 用二进制数组和多个哈希函数,判断一个 key 一定不存在或者可能存在。

优点: 内存占用小,效率高
缺点: 有一定误判率,不支持删除

4. 两种方案对比

方案 优点 缺点 适用场景
缓存空值 简单 浪费内存 数据量小
布隆过滤器 高效省内存 有误差 数据量大

5. 其他注意事项

  • 接口增加参数校验,不合法的 id 直接拒绝
  • 增加接口访问限流,防止恶意攻击
  • 黑名单机制,频繁查不存在的 IP 封掉

总结

缓存穿透解决思路:

  1. 缓存空值: 简单,适合小数据量
  2. 布隆过滤器: 高效,适合大数据量
  3. 加限流和校验: 从入口防护

生产环境推荐布隆过滤器 + 缓存空值组合使用。


emer 发布于  2026-10-8 07:06 

Redis 缓存穿透解决

Redis 缓存穿透解决方案

缓存穿透是指查询一个数据库里根本不存在的数据,因为缓存里也没有,所以每次请求都打到数据库。如果有人恶意攻击,数据库可能被打挂。

什么是缓存穿透

正常流程:先查缓存,缓存有就直接返回,没有再查数据库,然后写入缓存。

如果查询的数据数据库里也没有,那缓存永远都不会命中,每次请求都会查数据库,这就是缓存穿透。

解决方案一:缓存空值

最简单的办法,数据库查不到也往缓存里写一个空值,设个短过期时间。

$data = $redis->get($key);
if ($data === null) {
    $data = $db->query($sql);
    if ($data === null) {
        // 缓存空值,60秒过期
        $redis->setex($key, 60, '');
    } else {
        $redis->setex($key, 3600, $data);
    }
}

优点:简单直接
缺点:会占用更多缓存空间,大量空值缓存会浪费内存

解决方案二:布隆过滤器

在缓存前面加一层布隆过滤器,先判断数据是否存在,不存在直接返回。

布隆过滤器的原理是用一个大的位数组和多个哈希函数,把存在的数据标记为1。查询时如果所有哈希位置都是1,说明可能存在;只要有一个位置是0,说明一定不存在。

优点:内存占用小,效率高
缺点:有一定误判率(说存在可能不存在,说不存在一定不存在)

两种方案对比

方案 优点 缺点 适用场景
缓存空值 简单 浪费内存 数据量小,穿透不严重
布隆过滤器 内存效率高 有误差 数据量大,穿透严重

总结

缓存穿透是常见的缓存问题,小项目用缓存空值就够了,大项目数据量大就上布隆过滤器。两种方案都能解决问题,根据实际场景选就行。


emer 发布于  2026-10-4 10:43 
'); })();