高并发场景下的缓存雪崩探析与应对策略

目录

一、问题描述

二、解决策略分析

(一)解决策略一:分散过期时间

(二)解决策略二:提前演练压测

[(三)解决策略三:cache高可用 + 后端数据库限流](#(三)解决策略三:cache高可用 + 后端数据库限流)

三、总结


干货分享,感谢您的阅读!

在高并发场景下,缓存作为前置查询机制,显著减轻了数据库的压力,提高了系统性能。然而,这也带来了缓存失效、增加回溯率等风险。常见的问题包括缓存穿透、缓存雪崩、热Key和大Key等。这些问题如果不加以处理,会影响系统的稳定性和性能。因此,采用有效的缓存策略,如缓存空结果、布隆过滤器、缓存过期时间随机化、多级缓存等,对于保障系统在高并发情况下的可靠性至关重要。本次我们将详细探讨缓存雪崩及其应对策略。

一、问题描述

缓存层挡在db层前面,抗住了非常多的流量,在分布式系统中,"everything will fails",缓存作为一种资源,当cache crash后,流量集中涌入下层数据库,称之为缓存雪崩。

造成这种问题通常有2种原因:

  1. 业务层面:大量的缓存key同时失效,失效请求全部回源到数据库,造成数据库压力过大崩溃。
  2. 系统层面:缓存服务宕机。

二、解决策略分析

(一)解决策略一:分散过期时间

业务层面的原因,主要是缓存key过期时间一致,造成同一时间,大量缓存key同时失效。针对这种问题的解决方案,主要是防止缓存在同一时间一起过期,如在设置的过期时间的基础上增加1-5分钟的随机值,使缓存失效时间比较均匀。通过这种方式,可以避免缓存key在同一时间失效,进而防止大量请求同时回溯到数据库。

java 复制代码
import java.util.Random;

public class CacheManager {
    private static final Random RANDOM = new Random();

    // 设置缓存值并增加随机过期时间
    public void setCache(String key, Object value, int baseExpirationTimeInSeconds) {
        int randomOffset = RANDOM.nextInt(300); // 1-5分钟的随机值,单位是秒
        int finalExpirationTime = baseExpirationTimeInSeconds + randomOffset;
        // 这里假设使用的是某个缓存框架的set方法
        cache.set(key, value, finalExpirationTime);
    }
}

(二)解决策略二:提前演练压测

系统在实际运行中可能会遇到各种性能瓶颈,提前演练和压测可以帮助发现这些瓶颈并进行优化。

在系统上线前,进行充分的压力测试和演练,找出系统的性能瓶颈,预估合适的系统存储和计算容量。根据压测结果,调整系统架构和配置,确保在高并发情况下系统能够稳定运行。

(三)解决策略三:cache高可用 + 后端数据库限流

  • 采用双缓存热备份方案来进可能提升缓存资源的可用性
  • 后端数据库限流(Hystrix),缓存层宕机,流量集中打到数据库,会再次让db崩溃。为保护这种情况下的db,在db层加入限流(Hystrix)

具体实现如下:

java 复制代码
public class CacheSupplier {

    @Qualifier(value = "tairCache")
    @Autowired
    private HACache tair;

    @Qualifier(value = "squirrelCache")
    @Autowired
    private HACache squirrel;

    @Autowired
    private CacheConfig cacheConfig;

    // 当前主备缓存引用,默认 master(squirrel) slave(celler)
    private CachePair cachePair;

    public CachePair getCachePair() {
        if (!cacheConfig.isCacheOn()) {
            return null;
        }
        if (cachePair != null && !cacheConfig.changed()) {
            return cachePair;
        }
        return updateCachePair();
    }

    private synchronized CachePair updateCachePair() {
        if (cachePair == null || cacheConfig.changed()) {
            cacheConfig.getConfigLock().lock();
            try {
                cachePair = createCachePair();
                cacheConfig.unchanged();
            } finally {
                cacheConfig.getConfigLock().unlock();
            }
        }
        return cachePair;
    }

    private CachePair createCachePair() {
        CachePair cp = new CachePair();
        cp.setMaster(selectCache(cacheConfig.getMasterCache(), tair, squirrel));
        if (cacheConfig.isSlaveOn()) {
            cp.setSlave(selectCache(cacheConfig.getSlaveCache(), squirrel, tair));
        }
        return cp;
    }

    private HACache selectCache(String cacheName, HACache defaultCache1, HACache defaultCache2) {
        if (CacheConfig.TAIR.equals(cacheName)) {
            return tair;
        } else if (CacheConfig.SQUIRREL.equals(cacheName)) {
            return squirrel;
        } else {
            return defaultCache1 != null ? defaultCache1 : defaultCache2;
        }
    }
}

三、总结

缓存雪崩作为高并发场景下的一个典型问题,严重威胁着系统的稳定性和性能。当缓存层失效或服务宕机时,大量请求会直接涌入数据库,造成数据库负载过重甚至崩溃。因此,采取有效的应对策略至关重要。

本文首先明确了缓存雪崩的定义及其两种主要成因,即业务层面的大量缓存key同时失效和系统层面的缓存服务宕机。针对这些成因,提出了三种解决策略:

  1. 分散过期时间:通过在设置的过期时间基础上增加随机值,使缓存失效时间均匀分布,避免大量请求同时回溯到数据库。
  2. 提前演练压测:通过充分的压力测试和演练,找出系统的性能瓶颈,并在系统上线前进行优化,确保系统在高并发情况下的稳定运行。
  3. cache高可用和后端数据库限流:采用双缓存热备份方案提升缓存资源的可用性,并在数据库层加入限流机制,防止缓存层宕机时流量集中打到数据库,进而保护数据库的稳定性。

这些策略各有优劣,结合具体业务场景和系统特点,灵活运用这些策略可以有效防止缓存雪崩。

相关推荐
用户094248568031 天前
第19章:库存扣减与秒杀系统:Redis 扛峰值流量
redis·架构·源码
爱写Bug的浮游1 天前
RTA高频请求下的Redis短缓存工程闭环
redis
没有气的汽水1 天前
redis加了个分片,把线上写挂了一个多小时
redis
lsc_blog1 天前
年底还在招的岗位,先确认缺口和到岗要求
git·缓存·ci/cd·职场和发展·github
xixiaoyunya1 天前
Redis缓存三大问题实战:穿透、击穿、雪崩,我用一个电商秒杀场景讲清楚
数据库·redis·缓存
鶴哥只手遮天1 天前
《Redis 8.10.3 深度剖析》系列 · 第 1 章·全景与源码地图:版本谱系、构建体系、全局状态与启动链路
redis·后端·开源
70后写java的老码农1 天前
docker-compose 部署 Redis Stack Server 6.2.6-v19
redis·docker
小南家的青蛙1 天前
Redis监控与告警体系化实践
redis·监控方案·告警方案
我不会起名字3221 天前
Redis 从 0 到 1(二十七):JSON 体积是 Protobuf 的 3.7 倍?3 种序列化的 Go 基准测试
redis·go·序列化·protobuf·messagepack