当前位置: 首页 > article >正文

redis同步解决 缓存击穿+缓存穿透 原理代码实现

缓存穿透

 就是一个根本不存在的数据 请求过来,然后 发现缓存没有,就打到数据库,然后 数据库也没有,就会给数据库造成很大的压力 ,

解决方案 就是老生常谈的  返回null值,或者布隆过滤器  

我们说 返回null值     也就是 在查到数据库之后,发现数据库没有,就缓存一个null值到缓存,然后 返回回去,这样下一个请求过来了,就会读到我们redis中缓存的null值

缓存击穿

 一个热点 key 突然过期了,这时候有大量的请求突然访问过来,但是缓存过期了,请求直接打到数据库,造成数据库压力过大  

一般解决方案 有两种 

1. 用redis的互斥锁 ,保证缓存过期的时候,只有一个线程能访问数据库,然后 构建缓存,其他线程 用自旋锁一直挂起, 在挂起的时候,线程休眠一下,然后一直获取缓存 

2. 逻辑过期  原理很简单 既然缓存会过期,那你设置永不过期就行了,然后给类中添加 逻辑过期 时间,每次获得线程的时候 只要判断逻辑过期没有,逻辑过期了,就获取互斥锁,重建缓存

这样说 可能比较抽象 ,我们下面直接进行代码实现  ,保证解决缓存穿透的同时,再用互斥锁解决缓存击穿的问题

业务逻辑图

如果不理解,那就自己脑补一下多线程的情况

 下面是代码实现

获取锁的类

获得锁
 然后代码实现

根据id访问缓存

.

最后的逻辑实现
 //互斥锁解决缓存击穿
    private Result nXPassThrough(Long id) throws InterruptedException {
        Result shopMap = findShopMap(id);
        if (shopMap.getErrorMsg() == null) {
            return Result.ok(shopMap);
        }
        //解决缓存击穿
        //走到这里  代表着 缓存查不到了
        //1. 获取 redisson 互斥锁
        //2. 获取不成功 自旋等待
        while (true) {
            if (passThroughLock.tryLock()) {
                try {
                    log.info("获得到了锁");
                    //2. 获取成功查询数据库 返回缓存数据
                    //没有的话查数据库
                    Shop shopById = query().eq("id", id).one();
                    if (BeanUtil.isNotEmpty(shopById)) {
                        //数据库有的话 添加缓存并且返回
                        Result shopMap1 = findShopMap(id);
                        if (shopMap1.getErrorMsg() == null) {
                            return Result.ok(shopMap1);
                        }
                        Map<String, Object> stringObjectMap = BeanUtil.beanToMap(shopById);
                        redisTemplate.opsForHash().putAll(RedisConstants.CACHE_SHOP_KEY + id, stringObjectMap);
                        // 设置过期时间 防止内存 占满
                        redisTemplate.expire(RedisConstants.CACHE_SHOP_KEY + id, RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
                        return Result.ok(shopById);
                    }
                    //数据库没有 返回false
                    //解决缓存穿透   访问不存在的数据 缓存为null值 并且设置过期时间
                    log.info("缓存穿透 构建了新数据");
                    redisTemplate.opsForHash().put(RedisConstants.CACHE_SHOP_KEY + id, "nullId", "null");
                    redisTemplate.expire(RedisConstants.CACHE_SHOP_KEY + id, RedisConstants.CACHE_NULL_TTL, TimeUnit.SECONDS);
                    return Result.ok("返回成功");
                } finally {
                    passThroughLock.unlock();
                }
            } else {
                try {
                    Thread.sleep(100);
                    Result shopMap1 = findShopMap(id);
                    if (shopMap1.getErrorMsg() == null) {
                        return Result.ok(shopMap1);
                    }
                } catch (InterruptedException e) {
                    return Result.fail(e.getMessage());
                }
            }
        }
    }
jmter压测结果

当我们访问数据库存在的数据

当我们访问数据库不存在的数据

也是同样的情况只查询到了一次sql,并且构建了新的空数据


http://www.kler.cn/news/342001.html

相关文章:

  • go代码不生效问题
  • Java开发环境命名规则
  • 使用FastAPI做人工智能后端服务器时,接口内的操作不是异步操作的解决方案
  • 【rCore OS 开源操作系统】Rust 异常处理
  • 5款人声分离免费软件分享,从入门到精通,伴奏提取分分钟拿捏!
  • 基于微信小程序的像素画创作与分享平台设计与实现
  • 【实战】Nginx+Lua脚本+Redis 实现自动封禁访问频率过高IP
  • 10.10 题目总结(累计)
  • 大数据技术与应用实战
  • 算法学习4
  • 第十二章 Redis短信登录实战(基于Session)
  • 十二、血条UI
  • 第100+27步 ChatGPT学习:概率校准 Temperature Scaling
  • rabbitmq死信队列详解与使用
  • 猿人学 — 第1届第13题(解题思路附源码)
  • 普中51单片机
  • java速成指南
  • linux部署NFS和autofs自动挂载
  • pytest框架之fixture测试夹具详解
  • 【docker】要将容器中的 livox_to_pointcloud2 文件夹复制到宿主机上