Redis 缓存雪崩介绍

缓存雪崩(Cache Avalanche)是指在高并发的情况下,缓存的某一部分或者大量缓存数据同时过期或被清除,导致大量请求直接访问数据库,从而使数据库压力瞬间增加,造成系统性能严重下降,甚至引发数据库宕机。

缓存雪崩的原因

  1. 缓存同时过期:当大量的缓存数据设置了相同的过期时间,可能会导致这些缓存数据在同一时刻同时失效或过期。这时,大量请求会同时去访问数据库,造成数据库的负载急剧增加。
  2. 缓存清除:由于缓存清除策略(如 LRU 淘汰策略),大量缓存数据被同时清除,导致请求直接访问数据库。
  3. 集群中断:Redis 集群出现故障或者 Redis 节点宕机,导致缓存数据不可用,从而所有请求都需要访问数据库。

缓存雪崩的影响

  • 数据库压力剧增:大量请求直接访问数据库,可能会造成数据库负载过高,响应变慢,甚至数据库宕机。
  • 系统性能下降:数据库压力增大,系统的整体响应时间和吞吐量下降。
  • 服务不可用:数据库处理不过来请求时,可能导致服务崩溃或响应异常,影响用户体验。

如何解决缓存雪崩

  1. 缓存过期时间随机化:通过随机化缓存数据的过期时间,避免大量缓存同时过期,减少缓存雪崩的风险。
  2. 使用互斥锁:在缓存失效时,使用分布式锁确保只有一个请求会去查询数据库并更新缓存,避免大量请求同时访问数据库。
  3. 缓存预热:提前将一些常用或热点数据加载到缓存中,避免在高并发情况下数据库被压垮。
  4. 高可用缓存方案:使用 Redis 集群和 Sentinel 实现高可用,防止 Redis 单点故障导致缓存不可用。

1. 缓存过期时间随机化

为了防止大量缓存数据在同一时间过期,可以通过对缓存的过期时间进行随机化,使得不同的缓存数据有不同的过期时间,从而避免在某一时刻大量缓存数据同时失效。

代码实现:
import redis.clients.jedis.Jedis;
import java.util.Random;

public class CacheAvalancheExample {

    private static Jedis jedis = new Jedis("localhost", 6379);

    public static void main(String[] args) {
        String key = "user:1003";  // 假设查询用户 ID 为 1003
        String value = getFromCache(key);

        if (value == null) {
            // 如果缓存为空,查询数据库
            value = getFromDatabase(key);

            // 随机化过期时间,避免大量缓存同时过期
            Random random = new Random();
            int randomExpireTime = 3600 + random.nextInt(3600);  // 随机过期时间为1-2小时之间

            setToCache(key, value, randomExpireTime);
        }

        System.out.println("Fetched value: " + value);
    }

    // 从缓存获取数据
    public static String getFromCache(String key) {
        return jedis.get(key);
    }

    // 设置数据到缓存
    public static void setToCache(String key, String value, int expireTime) {
        jedis.setex(key, expireTime, value);  // 设置随机过期时间
    }

    // 模拟查询数据库
    public static String getFromDatabase(String key) {
        // 假设数据库查询返回数据
        return "User data for " + key;
    }
}

  1. 随机化过期时间:通过 random.nextInt(3600) 给每个缓存数据添加一个随机的过期时间,避免同一时刻大量缓存同时过期。
  2. 设置缓存:使用 setex 设置数据到 Redis 中,并设置随机的过期时间。

通过这种方法,缓存的过期时间不再是固定的,避免了大量数据在同一时刻过期,减轻了数据库的压力。

2. 使用分布式锁避免缓存雪崩

为了确保当缓存失效时,只有一个请求能够查询数据库并更新缓存,可以使用 Redis 的分布式锁来加锁,防止其他请求同时访问数据库。

代码实现:
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.Redisson;

public class CacheAvalancheWithLockExample {

    private static RedissonClient redisson;

    public static void main(String[] args) {
        // 配置 Redisson 客户端连接 Redis
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        redisson = Redisson.create(config);

        // 模拟并发访问数据库的操作
        String key = "user:1004";  // 假设查询用户 ID 为 1004
        String value = getUserFromCache(key);

        if (value == null) {
            // 缓存失效,查询数据库
            RLock lock = redisson.getLock(key + ":lock");

            lock.lock();
            try {
                // 如果缓存依然未命中,则查询数据库并更新缓存
                value = getUserFromCache(key);
                if (value == null) {
                    value = getUserFromDatabase(key);
                    setUserToCache(key, value);
                }
            } finally {
                lock.unlock();
            }
        }

        System.out.println("Fetched value: " + value);
        redisson.shutdown();
    }

    // 从缓存获取用户数据
    public static String getUserFromCache(String key) {
        return jedis.get(key);
    }

    // 设置用户数据到缓存
    public static void setUserToCache(String key, String value) {
        jedis.setex(key, 3600, value);  // 设置缓存过期时间为1小时
    }

    // 模拟查询数据库
    public static String getUserFromDatabase(String key) {
        // 假设数据库查询返回数据
        return "User data for " + key;
    }
}

  1. 加锁:当缓存失效时,通过 Redis 分布式锁(RLock)确保只有一个线程会查询数据库并更新缓存,避免多个线程同时访问数据库。
  2. 查询数据库:获取锁后,查询数据库并更新缓存。如果缓存仍然为空,则执行数据库查询。
  3. 锁释放:在数据库查询和缓存更新完成后,释放锁。

这种方法通过分布式锁,避免了高并发情况下数据库的压力,从而有效防止缓存雪崩。

3. 高可用缓存方案

为了防止 Redis 单点故障导致缓存不可用,可以采用 Redis 集群模式或 Redis Sentinel 模式来提供高可用性,确保即使某个 Redis 节点出现故障,系统也能够正常运行。

可以通过配置 Redis Sentinel 或 Redis Cluster 来实现高可用,具体实现过程需要配置多个 Redis 节点,确保在主节点出现故障时,自动进行故障转移。

Redis的碎碎念 文章被收录于专栏

Redis面试中的碎碎念

全部评论

相关推荐

点赞 评论 收藏
分享
评论
2
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务