Redis 防止缓存雪崩的几种策略

缓存雪崩(Cache Avalanche)会在大量缓存失效的情况下导致大量请求涌入数据库,造成数据库压力骤增甚至崩溃。为了防止缓存雪崩,我们可以采用以下几种策略来优化缓存管理

【加锁排队、数据预热、双层缓存策略、定时更新缓存策略、不同过期时间、热数据永不过期】:

1. 加锁排队

通过使用分布式锁来控制缓存失效时的请求流量,key: whiltList value:1000w个uid 指定setNx whiltList value nullValue mutex互斥锁解决,Redis的SETNX去set一个mutex key, 当操作返回成功时,再进行load db的操作并回设缓存; 否则,就重试整个get缓存的方法:

function getDataWithLock($key) {
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    $value = $redis->get($key);

    // 如果缓存存在,直接返回
    if ($value !== false) {
        return $value;
    }

    // 尝试获取分布式锁
    $lockKey = "lock:{$key}";
    $isLockAcquired = $redis->set($lockKey, 1, ['nx', 'ex' => 5]);

    if ($isLockAcquired) {
        // 锁成功,加载数据库数据并重设缓存
        $value = loadFromDatabase($key);
        $redis->set($key, $value, 600); // 设置缓存有效期为600秒

        // 释放锁
        $redis->del($lockKey);

        return $value;
    } else {
        // 锁失败,休眠一段时间再重试获取缓存
        usleep(50000); // 休眠50毫秒
        return getDataWithLock($key);
    }
}

function loadFromDatabase($key) {
    // 模拟从数据库加载数据
    return "数据库中的数据";
}

2. 数据预热

在系统上线或可能发生高并发前或者缓存预热就是系统上线后,手动加载热点数据到缓存中,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!可以通过缓存reload机制,预先去更新缓存,再即将发生大并发访问前手动触发加载缓存不同的key:

function preloadCache() {
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    $hotKeys = ['key1', 'key2', 'key3'];
    foreach ($hotKeys as $key) {
        $value = loadFromDatabase($key);
        $redis->set($key, $value, 3600); // 缓存有效期为1小时
    }
}

3. 双层缓存策略

使用两层缓存来提供更稳定的数据访问,C1为原始缓存,C2为拷贝缓存,C1失效时,可以访问C2,C1缓存失效时间设置为短期,C2设置为长期:

function getDataWithDoubleCache($key) {
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    // 一级缓存
    $value = $redis->get("C1:{$key}");

    if ($value !== false) {
        return $value;
    }

    // 二级缓存
    $value = $redis->get("C2:{$key}");
    if ($value !== false) {
        // 如果找到二级缓存的数据,更新一级缓存
        $redis->set("C1:{$key}", $value, 600);
        return $value;
    }

    // 缓存全部失效,从数据库加载
    $value = loadFromDatabase($key);
    $redis->set("C1:{$key}", $value, 600); // 一级缓存短期有效
    $redis->set("C2:{$key}", $value, 3600); // 二级缓存长期有效

    return $value;
}

4. 定时更新缓存策略

使用定时任务来定期更新缓存,或对失效性要求不高的缓存,容器启动初始化加载,采用定时任务更新或移除缓存:

function scheduledCacheUpdate() {
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    $keysToRefresh = ['key1', 'key2', 'key3'];
    foreach ($keysToRefresh as $key) {
        $value = loadFromDatabase($key);
        $redis->set($key, $value, 3600); // 更新缓存
    }
}

5. 不同过期时间

通过设置不同的缓存过期时间来防止所有缓存同时失效,让缓存失效的时间点尽量均匀:

function setCacheWithRandomExpiry($key, $value) {
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    $expiryTime = rand(3600, 7200); // 随机设置1到2小时的过期时间
    $redis->set($key, $value, $expiryTime);
}

6. 热点数据永不过期

对访问频繁的热点数据设置永不过期:

function setHotDataCache($key, $value) {
    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);

    // 热点数据设置为永不过期
    $redis->set($key, $value);
}

总结

以上策略可以在实际应用中灵活组合使用,以减少缓存雪崩的风险并保护数据库。通过分布式锁、缓存预热、双层缓存、定时更新、不同过期时间以及对热点数据的特殊处理,可以有效地控制缓存失效带来的压力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值