当前位置:网站首页>分布式锁-Redission - 缓存一致性解决

分布式锁-Redission - 缓存一致性解决

2022-08-11 06:54:00 花棉袄

🧑‍ 个人主页:花棉袄

本章内容:【Redission - 缓存一致性解决
版权: 本文由【花棉袄】原创在CSDN首发需要转载请联系博主

在这里插入图片描述

Redission - 缓存一致性解决

️如果文章对你有帮助【关注点赞️收藏】

双写模式

在这里插入图片描述

  • 两个线程写 最终只有一个线程写成功,后写成功的会把之前写的数据给覆盖,这就会造成脏数据

失效模式

在这里插入图片描述

三个连接

一号连接 写数据库 然后删缓存

二号连接 写数据库时网络连接慢,还没有写入成功

三号链接 直接读取数据,读到的是一号连接写入的数据,此时 二号链接写入数据成功并删除了缓存,三号开始更新缓存发现更新的是二号的缓存

解决方案

无论是双写模式还是失效模式,都会到这缓存不一致的问题,即多个实力同时更新会出事,怎么办?

1、如果是用户纯度数据(订单数据、用户数据),这并发几率很小,几乎不用考虑这个问题,缓存数据加上过期时间,每隔一段时间触发读的主动更新即可
2、如果是菜单,商品介绍等基础数据,也可以去使用 canal 订阅,binlog 的方式
3、缓存数据 + 过期时间也足够解决大部分业务对缓存的要求
4、通过加锁保证并发读写,写写的时候按照顺序排好队,读读无所谓,所以适合读写锁,(业务不关心脏数据,允许临时脏数据可忽略)
  • 总结:
我们能放入缓存的数据本来就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保证每天拿到当前的最新值即可
我们不应该过度设计,增加系统的复杂性
遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点

配置Redission

  • 引入依赖
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.5</version>
</dependency>
  • 自定义配置类
@Configuration
public class MyRedissonConfig {
    
    /** * 对 Redisson 的使用都是通过 RedissonClient 对象 * @return * @throws IOException */
    @Bean(destroyMethod="shutdown") // 服务停止后调用 shutdown 方法。
    public RedissonClient redisson() throws IOException {
    
        // 1.创建配置
        Config config = new Config();
        // 集群模式
        // config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");
        // 2.根据 Config 创建出 RedissonClient 示例。
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        return Redisson.create(config);
    }
}
  • 使用分布式锁Redission解决缓存一致性问题
  public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock() {
    
        String uuid = UUID.randomUUID().toString();
        // 设置值同时设置过期时间
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);
        if (lock) {
    
            // 加锁成功..执行业务
            // 设置过期时间,必须和加锁是同步的,原子的
            Map<String, List<Catelog2Vo>> dataFromDb;
            try {
    
                dataFromDb = getCatelogJsonFromDb();
            } finally {
    
                String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                //删除锁
                Long lock1 = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);
            }
            return dataFromDb;
        } else {
    
            // 加锁失败,重试 synchronized()
            // 休眠200ms重试
            System.out.println("获取分布式锁失败,等待重试");
            try {
    
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
    
                e.printStackTrace();
            }
            return getCatalogJsonFromDbWithRedisLock();
        }
    }

    //三级分类
    public Map<String, List<Catelog2Vo>> getCatelogJsonFromDb() {
    
            // 0.得到锁之后从缓存中获取
            String catelogJSON = redisTemplate.opsForValue().get("categoryJSON");
            if (!StringUtils.isEmpty(catelogJSON)) {
    
                // 转换为我们指定的对象
                Map<String, List<Catelog2Vo>> result = JSON.parseObject(catelogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {
    
                });
                System.out.println("获得锁-从缓存中获取");
                return result;
            }
            System.out.println("获得锁-查询了数据库");
            //将数据库的多次查询变为一次查询
            List<CategoryEntity> selectList = baseMapper.selectList(null);
            //1.查询所有一级分类
            List<CategoryEntity> categoryOne = getParent_cid(selectList, 0L);
            //2.封装数据
            Map<String, List<Catelog2Vo>> collect = categoryOne.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {
    
                //2.1根据一级分类ID查询二级分类
                List<CategoryEntity> categoryTwo = getParent_cid(selectList, v.getCatId());
                List<Catelog2Vo> catelog2Vos = null;
                if (categoryTwo != null) {
    
                    //2.2 封装二级分类
                    catelog2Vos = categoryTwo.stream().map(levelTwo -> {
    
                        Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), null, levelTwo.getCatId().toString(), levelTwo.getName().toString());
                        List<CategoryEntity> categoryThree = getParent_cid(selectList, levelTwo.getCatId());
                        if (categoryThree != null) {
    
                            List<Catelog2Vo.Catelog3Vo> catalog3List = categoryThree.stream().map(levelThree -> {
    
                                //2.3 封装三级分类
                                Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo(levelTwo.getCatId().toString(), levelThree.getCatId().toString(), levelThree.getName().toString());
                                return catelog3Vo;
                            }).collect(Collectors.toList());
                            //设置三级分类
                            catelog2Vo.setCatalog3List(catalog3List);
                        }
                        return catelog2Vo;
                    }).collect(Collectors.toList());
                }
                return catelog2Vos;
            }));
            return collect;
        }
原网站

版权声明
本文为[花棉袄]所创,转载请带上原文链接,感谢
https://blog.csdn.net/m0_46914264/article/details/126250341