【Redis进阶】缓存应用

目录

缓存击穿

概念

缓存击穿的原因

缓存击穿的影响

缓存击穿的应对措施

设置分布式锁

提前更新缓存

请求分级和降级

缓存穿透

概念

缓存穿透的原因

缓存穿透的应对措施

缓存空值

布隆过滤器

 限流和黑名单

缓存雪崩

缓存雪崩概念

缓存雪崩的原因

应对措施

缓存过期时间随机化:

多级缓存架构

限流

预热缓存


缓存击穿

概念

缓存击穿是指缓存中不存在但数据库中存在的数据请求,由于缓存没有命中,导致请求直接落到数据库上,造成数据库压力骤增。缓存击穿常常发生在某些热点数据上,比如某个高访问频率的热点键突然失效时,大量请求直接穿透缓存访问数据库。

缓存击穿的原因

热点数据缓存失效:高访问频率的热点数据在缓存中失效,导致大量请求直接访问数据库。

缓存设置不当:如缓存策略不合理,过期时间设置过短等

突发流量:突发性的大量请求几种访问某个热点数据,导致缓存来不及重建,直接击穿到数据库。

缓存击穿的影响

数据库压力骤增:大量请求直接访问数据库,可能导致数据库负载过高甚至崩溃。

响应延迟增加:缓存击穿会导致请求需要经过数据库查询,响应时间变长,影响用户体验。

系统稳定性降低:数据库过载可能引发系统不稳定,甚至导致服务不可用。

缓存击穿的应对措施

设置分布式锁

在缓存失效时,通过加锁机制,确保只有一个线程去加载数据并更新缓存,其他线程等待锁释放后再读取缓存。

提前更新缓存

在缓存失效前主动更新缓存。可以使用定时任务或异步任务,在缓存即将过期时提前刷新缓存。

请求分级和降级

  • 针对缓存击穿的情况,可以对请求进行分级处理,优先保证核心请求。
  • 对非核心请求进行降级处理,如返回默认值或错误提示。

缓存穿透

概念

缓存穿透是指请求的数据既不在缓存中,也不存在于数据库中,导致每次请求都直接达到数据库。由于这些请求无法通过缓存过滤掉,所有的请求都到达数据库。可能会对数据库造成巨大压力。

缓存穿透的原因

恶意攻击:攻击者故意构造大量不存在的key进行请求,绕过缓存,直接打到数据库,造成数据库压力骤增。

业务设计缺陷:正常业务逻辑中,存在大量的不存在的数据请求,未合理处理,导致频繁穿透缓存。

用户错误输入:用户输入错误或者不规范的数据,导致请求的数据在缓存和数据库中都不存在。

缓存穿透的应对措施

缓存空值

  • 对于查询结果为空的数据,也将其缓存起来,避免下一次请求再次穿透过数据库。
String key = "nonExistingKey";
String value = redis.get(key);

if (value == null) {
    value = db.get(key);
    if (value == null) {
        // 将空值写入缓存,并设置合理的过期时间
        redis.set(key, "", 60); // 60秒过期
    } else {
        redis.set(key, value, 300); // 300秒过期
    }
}

布隆过滤器

  • 使用布隆过滤器在缓存前进行一次快速判断,如果布隆过滤器判断该 key 不存在,则直接返回,不再查询缓存和数据库。
// 初始化布隆过滤器
BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), expectedInsertions);

// 添加存在的key到布隆过滤器
bloomFilter.put("existingKey1");
bloomFilter.put("existingKey2");

String key = "nonExistingKey";
if (!bloomFilter.mightContain(key)) {
    return null; // 直接返回,不查询缓存和数据库
}

// 正常的缓存和数据库查询流程
String value = redis.get(key);
if (value == null) {
    value = db.get(key);
    redis.set(key, value);
}

 限流和黑名单

  • 对于明显的恶意请求,可以采取限流和黑名单策略,限制其访问频率或直接拒绝其请求。
String clientIp = getClientIp();
if (isBlacklisted(clientIp)) {
    return null; // 直接返回,不查询缓存和数据库
}

// 正常的缓存和数据库查询流程
String key = "someKey";
String value = redis.get(key);
if (value == null) {
    value = db.get(key);
    redis.set(key, value);
}

缓存雪崩

缓存雪崩概念

缓存雪崩是指由于大量缓存数据在同一时间过期或失效,导致后续的所有请求都直接访问数据库,瞬间增加数据库的负载。与缓存击穿不同,缓存雪崩通常影响的是大批量的缓存数据,而不是单个热点数据。

缓存雪崩的原因

  • 缓存过期时间设置不合理:大批量缓存键设定了相同或相近的过期时间,导致它们在同一时间点失效。
  • 突发流量:在流量高峰期,如果缓存失效,短时间内会有大量请求同时涌入数据库。
  • 缓存服务宕机:缓存服务(如 Redis 实例)出现宕机或重启,导致所有缓存失效,所有请求直接击中数据库。

应对措施

缓存过期时间随机化:

  • 在设置缓存过期时间时,添加一定的随机时间,使得不同的缓存键有不同的过期时间,避免大量缓存键在同一时间失效。
// 伪代码示例
int baseExpireTime = 600; // 基础过期时间600秒
int randomTime = new Random().nextInt(300); // 随机附加300秒
int expireTime = baseExpireTime + randomTime;
redis.set(key, value, expireTime);

多级缓存架构

  • 通过引入本地缓存(如 Guava Cache)和分布式缓存(如 Redis)相结合的多级缓存架构,减少直接访问数据库的请求数量。
// 伪代码示例
String key = "someKey";
String value = localCache.get(key);

if (value == null) {
    value = redis.get(key);
    if (value == null) {
        value = db.get(key);
        redis.set(key, value);
    }
    localCache.put(key, value);
}

限流

  • 通过限流机制控制高并发请求的流量,防止因大量请求同时访问数据库而导致数据库崩溃。
// 伪代码示例
if (!rateLimiter.tryAcquire()) {
    return "Service is busy, please try again later.";
}

String key = "someKey";
String value = redis.get(key);

if (value == null) {
    value = db.get(key);
    redis.set(key, value);
}

return value;

预热缓存

  • 在高峰期来临之前,预先将热点数据加载到缓存中,避免在流量高峰期缓存失效导致的雪崩。
// 伪代码示例
@Scheduled(fixedRate = 60000) // 每分钟执行一次
public void preheatCache() {
    String key = "hotKey";
    String value = db.get(key);
    redis.set(key, value);
}

希望本文能帮助大家更好地理解和应对 Redis 缓存击穿,缓存穿透,缓存雪崩问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值