【技术精华】如何解决缓存击穿?

本篇文章内容包括:

  • 常规思路
  • 潜在问题
  • 缓存击穿
  • 什么是缓存击穿
  • 缓存击穿的原因
  • 缓存击穿的解决方案
  • 使用分布式锁

常规思路

在类似于电商系统中,会存在这样一种情况,比如某个冷门的商品,平时没有多少人去访问,然后公司将这些性价比高的冷门商品进行挖掘出来

然后将这些商品去交给热门的大主播来进行带货,大家看了主播的讲解后觉得东西确实很好,导致一瞬间的流程激增。

又比如某个顶流的明星突然宣布了爆炸性的新闻,比如结婚了、离婚了、劈腿了等等,大家会同一时间去查看类似的新闻,系统的流量同样会激增,前几年微博就是类似在这种情况下宕机了。

在这种情况下,访问数据库时就会造成的数据库的压力过大,很可能会直接将数据库宕机掉

有人说了,这时加个Redis缓存来缓解数据库的压力不就好了吗?既然这样,那就模拟一下写一段伪代码:

java 复制代码
public String getData(String id){
    RedisTemplate<String,String> redisTemplate = redisCache.getInstance();
    String cachedValue = redisTemplate.opsForValue().get(id);
    if (StringUtil.isEmpty(cachedValue)) {
        Program program = programMapper.selectById(id);
        if (Objects.nonNull(program)) {
            redisTemplate.opsForValue().set(id,JSON.toJSONString(program));
            cachedValue = JSON.toJSONString(program);
        }
    }
    return cachedValue;
};

首先从redis中查看是否存在,如果不存在则从数据库中查询,接着将查询出的数据放在redis中,最后将数据返回

不少人就是直接这么用的,到这里先不急着往下看,我们可以先仔细想一想,这么写会不会存在问题?

潜在问题

这种写法,在小公司或者并发量不高的情况下没有什么问题,但是在海量的并发场景下就会存在很严重的问题!我们再好好分析这段代码

java 复制代码
public String getData(String id){
    RedisTemplate<String,String> redisTemplate = redisCache.getInstance();
    String cachedValue = redisTemplate.opsForValue().get(id);
    
    //有一种极大的可能性,就是大量的请求都集中在这里判断redis中都是不存在的

    if (StringUtil.isEmpty(cachedValue)) {

        //大量的请求很可能会执行到这里
        
        Program program = programMapper.selectById(id);
        if (Objects.nonNull(program)) {
            redisTemplate.opsForValue().set(id,JSON.toJSONString(program));
            cachedValue = JSON.toJSONString(program);
        }
    }
    return cachedValue;
};

因为都是并发的请求,当一瞬间大量的请求过来时,很有可能大量的请求都是在 查询redis中的数据判断是不存在的,那么还是最终都去查询数据库了,虽然第一次请求从数据库中查询,然后放入到redis中,但其他的请求已经执行了redis判断的阶段,也已经去查询数据库了

也就是说这时加缓存完全没啥用了,最终导致的结果是虽然缓存中有数据,但还是大量的请求执行到了数据库层面,这就是常说的 缓存击穿!

缓存击穿

在微服务架构中,Redis等缓存系统被广泛用于提高系统的响应速度和减轻数据库的压力。通过将热点数据存储在缓存中,系统可以直接从缓存中获取数据,而无需每次都查询数据库,从而大大提高了系统的性能。然而,在使用缓存的过程中,也会遇到一些问题,其中之一就是缓存击穿问题。

什么是缓存击穿

缓存击穿是指当缓存中没有某个热点数据的缓存时(一种情况是缓存时间到期,另一种是大量请求集中在程序某一处判定为缓存不能存在),而此时恰好有大量的并发请求请求这个数据,这些请求都会直接打到数据库上,造成数据库短时间内承受大量请求而崩掉。

缓存击穿的原因

  • 缓存失效:当缓存中的数据因为过期时间到达而被删除,而新的缓存数据还没有被加载进来时,如果此时有大量的请求到来,这些请求都会直接打到数据库上,造成缓存击穿。
  • 缓存未命中:由于某些原因(如数据更新、缓存被误删等),缓存中不存在某个数据的缓存,当大量请求同时访问这个数据时,它们都会穿透缓存直接访问数据库。
  • 对于第一次请求:当第一次请求时,会判断缓存中没有存在这个数据,请求就会直接落到数据库上,当瞬间有大量的请求时,很有可能都会判断缓存中没有数据,直接全都打到了数据库层面上(开头举例就是此情况)

缓存击穿的解决方案

  • 设置热点数据永远不过期: 这是一种简单直接的解决方案,但是会导致缓存中的数据无法实时更新,可能引发数据不一致的问题
  • 分布式锁: 当缓存失效时,不是立即去加载数据库数据,而是先使用分布式锁去获取加载数据的权限,当获取到权限后,再去加载数据到缓存。这样,在第一个请求去加载数据时,其他并发请求则需要等待,第一个请求将数据加载到缓存后,直接释放分布式锁,此时其他请求就能够从缓存中获取数据,而不需要再去查询数据库

解决方案的分析

  • 第一种方案数据不过期肯定是不行的,就算只存放热点数据,但是也会随着数据量越来越大的情况下,redis的承受压力也会越来越大
  • 第二种使用分布式锁让请求进行串行化,确实可以解决缓存击穿的问题,我们来写一段代码实现使用分布式锁的方案

使用分布式锁

java 复制代码
public String getDataV2(String id){
    RedisTemplate<String,String> redisTemplate = redisCache.getInstance();
    String cachedValue = redisTemplate.opsForValue().get(id);
    if (StringUtil.isEmpty(cachedValue)) {
        //分布式锁
        RLock lock = serviceLockTool.getLock(LockType.Reentrant, id);
        lock.lock();
        try {
            Program program = programMapper.selectById(id);
            if (Objects.nonNull(program)) {
                redisTemplate.opsForValue().set(id,JSON.toJSONString(program));
                cachedValue = JSON.toJSONString(program);
            }
        } finally {
            lock.unlock();
        }
    }
    return cachedValue;
}

使用分布式锁的方式,就只能允许同一时刻只有一个请求来从数据库中查询数据,然后设置到缓存中,而其他的请求都要等待获得锁的请求执行完毕,这样大量的请求就不会同时到数据库层面

大家仔细想想,这段代码还存在着什么问题?有什么可以优化的地方吗?欢迎评论区交流!



🌸🌸🌸 完结撒花🌸🌸🌸

博主WX:g2279605572 欢迎大家与我交流!

相关推荐
va学弟几秒前
Java 网络通信编程(8):完善 UDP 协议
java·开发语言·udp
夫礼者4 分钟前
【极简监控】打破中间件黑盒:用 Micrometer 打造“SLF4J式”的降维打击Metrics监控体系
java·中间件·监控·metrics·micrometer
yashuk5 分钟前
Spring Boot 3.4 正式发布,结构化日志!
java·spring boot·后端
XDHCOM2 小时前
Redis远程连接命令详解,分享高效配置与安全实践技巧
前端·redis·安全
li99yo4 小时前
3DGS的复现
图像处理·pytorch·经验分享·python·3d·conda·pip
浅念-7 小时前
Linux 开发环境与工具链
linux·运维·服务器·数据结构·c++·经验分享
2501_926978338 小时前
AI的三次起落发展分析,及未来预测----理论5.0的应用
人工智能·经验分享·笔记·ai写作·agi
软件供应链安全指南8 小时前
以AI治理AI|问境AIST首家通过信通院大模型安全扫描产品能力评估!
人工智能·安全·ai安全·问境aist·aist·智能体安全
daidaidaiyu9 小时前
JMS, ActiveMQ 学习一则
java
weixin_704266059 小时前
SpringBoot全注解开发指南
java·spring boot·mybatis