Java 篇

Redis 篇

original icon
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.knowledgedict.com/tutorial/software_architect-redis-cache-avalanche.html

Redis 缓存雪崩原因以及解决方法详解


缓存雪崩(cache avalanche)主要指当某一时刻发生大规模不同 key 的缓存失效时,会有大量的请求进来直接打到 db 上,进而导致数据库压力增大,若在高并发的情况下,可能瞬间就会导致 database 宕机。

原因

Redis 缓存雪崩主要有如下两个原因:

  1. Redis 缓存服务器是正常的,但是大量热点数据同时过期,导致大量请求需要查询数据库并写到缓存;
  2. Redis 缓存服务器故障宕机,缓存系统异常。

redis 缓存雪崩

解决方法

redis key 同一时间大面积失效

避免大量热点缓存 key 同时失效,在为 redis key 设置有效期时,添加一个随机值(可以是 1-5 分钟),以使缓存失效时间均匀分布。这样,每个缓存的过期时间的重复率就会降低,进而避免 redis key 集体失效事件。

redis.set(key, value, fix_timeout + random);

针对大面积 key 同时过期,还有另一种方法,那就是热点数据不设置过期时间或设置比较长的失效时间,每个数据记录都有对应的缓存是否过期标记,如果已经过期,会触发另一个线程在后台更新实际 key 的缓存。

实际操作中,缓存数据其过期时间是缓存标记的几倍,如两倍。例如,标记缓存时间为 30 分钟,数据缓存设置为 60 分钟。这样,当缓存标记键过期时,实际缓存可以将旧数据返回给调用者,直到另一个线程完成后台更新后才会返回新缓存。

    public Object mockGetDataFromCache(long itemId) {

        int cacheTime = 30;
        String cacheKey = "p" + itemId;
        //  cache flag
        String cacheFlag = cacheKey + "_f";

        String flag = redis.get(cacheFlag);
        //  get cache value
        String cacheValue = redis.get(cacheKey);
        if (flag != null) {
            // Not expired, return directly
            return cacheValue;
        } else {
            redis.setex(cacheFlag, cacheTime, "1");
            if (cacheValue != null) {
                ThreadPool.execute((arg) -> {
                    //  This is generally SQL query data
                    String newData = getFromDB();
                    //  The time is set to twice the cache time for dirty reading
                    redis.setex(cacheKey, cacheTime * 2, newData);
                });
                return cacheValue;
            }
            String newData = getFromDB();
            //  The time is set to twice the cache time for dirty reading
            redis.setex(cacheKey, cacheTime * 2, newData);
            return newData;
        }
    }

如果对系统的性能及吞吐量没有较高的要求,也可以通过队列锁定来减轻数据库的压力。

Redis 宕机

redis 挂了,可以考虑提前实现 redis 的集群,使用集群缓存,保证缓存服务的高可用。

redis 集群方式有很多种,如 twemproxy 代理方式、codis 架构、redis cluster、Redis Sentinel(哨兵模式)等等,更详细的信息参考 redis 集群方式全面详解

此外,要开启 Redis 持久化机制,尽快恢复缓存集群,一旦重启,就能从磁盘上自动加载数据恢复内存中的数据。

其他

除了远程缓存如 redis、mongo 之类,也可以考虑结合本地缓存一起搭配使用,可以形成多层次的缓存机制,有效保证系统的稳定性,本地缓存有 guava 的 cache、ehcache 框架等。