Redis 缓存雪崩解决

Redis 缓存雪崩解决

缓存雪崩是指大量缓存同时失效,请求全部打到数据库,数据库直接挂掉。

1. 什么是缓存雪崩

原理:

  • 大量 key 同时过期
  • 请求都查数据库
  • 数据库压力瞬间暴增
  • 数据库扛不住挂掉

和缓存穿透的区别:

  • 穿透:查询不存在的数据
  • 雪崩:大量缓存同时失效

2. 产生原因

1)缓存大面积过期:

  • 大量数据设置了相同过期时间
  • 到时间一起失效

2)Redis 宕机:

  • Redis 服务挂了
  • 所有请求都走数据库

3. 解决方案

方案一:过期时间加随机值

// 不要设置相同过期时间
$expire = 3600 + rand(0, 600);
$redis->setex("key", $expire, $value);

加随机值,让 key 分散过期,不会同时失效。

方案二:缓存永不过期

  • 热点数据不设置过期时间
  • 更新数据时主动更新缓存
  • 异步更新缓存

方案三:Redis 高可用

  • 搭建 Redis 集群
  • 主从复制
  • 哨兵模式
  • 一台挂了还有其他的

方案四:熔断降级

  • 请求量太大时直接返回默认值
  • 不让请求打到数据库
  • 保护数据库

4. 代码示例

function getData($id) {
    global $redis, $db;

    $key = "data:".$id;
    $data = $redis->get($key);

    if ($data) {
        return json_decode($data, true);
    }

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

    // 缓存,过期时间加随机值
    $expire = 3600 + rand(0, 600);
    $redis->setex($key, $expire, json_encode($data));

    return $data;
}

5. 最佳实践

  • 过期时间加随机值
  • 搭建 Redis 集群
  • 热点数据永不过期
  • 加熔断降级机制
  • 数据库做限流

总结

缓存雪崩解决要点:

  • 过期时间加随机值,不要同时失效
  • Redis 做主从高可用
  • 热点数据不设置过期
  • 做好降级兜底

做高并发系统,缓存雪崩一定要防。


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

Redis 缓存雪崩解决

Redis 缓存雪崩解决方案

缓存雪崩是 Redis 最常见的线上事故之一,搞懂怎么预防,关键时刻能救命。

1. 什么是缓存雪崩

大量缓存同时过期,或者 Redis 整个挂了,所有请求都打到数据库,数据库瞬间压力爆炸,直接崩溃。

就像雪崩一样,一层压一层,全完了。

2. 和缓存穿透、击穿的区别

穿透:查不存在的数据,缓存挡不住,一直打数据库。

击穿:一个热点 key 突然过期,瞬间大量请求打数据库。

雪崩:大量 key 同时过期,或者 Redis 挂了,整个数据库被打垮。

3. 解决方案一:过期时间加随机值

最常用的方法。不要所有 key 都设一样的过期时间,在基础时间上加随机数:

// 不好:所有 key 都 1 小时过期
$redis->setex('product:1', 3600, $data);

// 好:基础 1 小时,加 0-600 秒随机
$ttl = 3600 + rand(0, 600);
$redis->setex('product:1', $ttl, $data);

这样过期时间分散开,不会一起失效。

4. 解决方案二:热点数据永不过期

热点数据不要主动设过期,后台异步更新:

// 商品详情这种热点数据,不设过期
$redis->set('product:1', $data);

// 后台定时任务更新
// 不依赖过期时间,由程序控制刷新

5. 解决方案三:Redis 高可用

Redis 自己要高可用,不能单点:

  • 主从架构 + 哨兵,主挂了自动切从
  • Redis Cluster 集群,分片存储,一个节点挂了不影响全局
# Redis Sentinel 哨兵配置
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000

6. 解决方案四:熔断降级

数据库撑不住的时候,要能自动降级:

  • 限流:用令牌桶、漏桶算法,限制请求量
  • 降级:返回默认数据或者"系统繁忙"
  • 熔断:数据库连续报错多了,直接不访问数据库

常用工具:Sentinel、Hystrix。

7. 解决方案五:多级缓存

不要只靠 Redis 一层缓存:

用户请求 → 浏览器缓存 → CDN → Nginx 本地缓存 → Redis → 数据库

多级缓存,就算 Redis 挂了,前面还有 Nginx 缓存挡着。

8. 应急处理

真出问题了怎么办:

  1. 先限流,别让数据库被打死
  2. 恢复 Redis 服务
  3. 重新预热热点数据
  4. 慢慢放开流量

总结

缓存雪崩解决方案:

  • 过期时间加随机值,别一起失效
  • 热点数据永不过期,后台更新
  • Redis 做高可用集群
  • 加熔断限流兜底
  • 多级缓存层层防护

记住这几点,缓存雪崩就不怕了。


emer 发布于  2026-10-6 07:39 

Redis 缓存策略:穿透、击穿、雪崩的完整解决方案

前言

Redis 缓存是高并发系统的标配,但用不好反而会出大问题。缓存穿透、击穿、雪崩这三个经典问题,几乎每个后端开发都踩过坑。本文用实战的方式,带你彻底搞懂这三个问题的原理和解决方案。

一、缓存穿透

什么是穿透

查询一个数据库里根本不存在的数据,缓存里也没有,每次请求都打到数据库上。这就是缓存穿透。

比如有人恶意请求 id = -1 的用户数据,数据库里永远查不到,缓存里也永远没有,每次请求都要查一次数据库。

解决方案

1. 缓存空值

function getUserById($id) {
    $user = $redis->get("user:{$id}");

    if ($user === false) {
        // 数据库查询
        $user = $db->query("SELECT * FROM users WHERE id = ?", [$id])->fetch();

        if ($user) {
            $redis->setex("user:{$id}", 3600, json_encode($user));
        } else {
            // 空值也缓存,短时间过期
            $redis->setex("user:{$id}", 60, 'NULL');
        }
    }

    return $user === 'NULL' ? null : $user;
}

优点:简单直接
缺点:如果恶意请求的 id 非常多,会占用大量缓存空间

2. 布隆过滤器

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

// 初始化布隆过滤器,把所有存在的 id 放进去
$bloomFilter = new RedisBloomFilter($redis);
$bloomFilter->add($allUserIds);

function getUserById($id) {
    // 先查布隆过滤器,不存在直接返回
    if (!$bloomFilter->exists($id)) {
        return null;
    }

    // 再走缓存查询...
}

优点:占用空间小,效率高
缺点:有一定误判率,实现复杂

二、缓存击穿

什么是击穿

某个热点 key 突然过期了,这一瞬间大量请求同时打到数据库上,就像被击穿了一样。

和穿透的区别:穿透是数据不存在,击穿是数据存在但缓存过期了。

解决方案

1. 互斥锁

function getHotProduct($id) {
    $product = $redis->get("product:{$id}");

    if ($product === false) {
        // 尝试获取锁
        $lockKey = "lock:product:{$id}";
        $isLocked = $redis->set($lockKey, 1, ['NX', 'EX' => 10]);

        if ($isLocked) {
            // 拿到锁,查数据库
            $product = $db->query("SELECT * FROM products WHERE id = ?", [$id])->fetch();
            $redis->setex("product:{$id}", 3600, json_encode($product));
            $redis->del($lockKey);
        } else {
            // 没拿到锁,等一会再查缓存
            usleep(10000);
            return getHotProduct($id);
        }
    }

    return $product;
}

优点:保证只有一个请求查数据库
缺点:实现复杂,有死锁风险

2. 热点数据永不过期

对真正的热点数据,直接设置永不过期,由后台异步更新。

// 热点数据不设过期时间,后台定时更新
$redis->set("hot_product:{$id}", json_encode($product));

三、缓存雪崩

什么是雪崩

大量缓存 key 在同一时间集体过期,或者 Redis 整个宕机了,所有请求瞬间全部打到数据库上,数据库直接被压垮。

和击穿的区别:击穿是一个热点 key,雪崩是大量 key 同时失效。

解决方案

1. 过期时间加随机值

// 不要所有 key 都设成 3600 秒过期
$ttl = 3600 + rand(0, 600);  // 在 1 小时基础上加 0-10 分钟随机值
$redis->setex("product:{$id}", $ttl, json_encode($product));

这样就不会出现大量 key 同一时间过期的情况。

2. Redis 集群高可用

部署 Redis 主从 + 哨兵,或者 Redis Cluster,确保 Redis 本身不会单点故障。

3. 服务降级限流

当数据库压力过大时,主动限流,返回兜底数据。

// 简单的降级逻辑
if ($redis->ping() === false) {
    // Redis 挂了,直接返回默认数据
    return getDefaultProduct();
}

四、三种问题对比

问题 原因 特点 解决方案
穿透 数据不存在 查不到的数据一直被请求 缓存空值 / 布隆过滤器
击穿 热点 key 过期 单个热点 key 瞬间失效 互斥锁 / 永不过期
雪崩 大量 key 同时过期 大面积缓存失效 随机过期时间 / 集群 / 限流

总结

缓存三件套的核心思路:

  1. 穿透:不让不存在的数据反复打数据库
  2. 击穿:热点数据过期时只让一个请求去查数据库
  3. 雪崩:让缓存失效时间错开,同时保证 Redis 高可用

搞懂这三个问题,你的缓存方案才算真正入门了。


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