Redis高级篇(缓存双写一致性)

1、双写一致性 业务代码逻辑实现

/**
     * Redis 双写一致性 业务逻辑代码
     *
     * 1、先查询Redis 若Redis存在 则直接返回
     * 2、若Redis不存在 则查询mysql 若mysql不存在 返回null (对不存在的key做标记,以防止缓存击穿)
     * 3、若mysql存在 则还需将mysql数据回写到Redis
     *
     * @param id
     * @return
     */
    @Override
    public User selectOneById(Integer id) {

        User user = null;

        // 1.先查询Redis 若Redis存在 则直接返回
        String key = CHECK_USER_KEY + id;
        user = (User) redisTemplate.opsForValue().get(key);

        // 2.若Redis不存在 则查询mysql 若mysql不存在 返回null
        if (user == null){
            user = userDao.selectOne(id);
            if (user == null) {

                return null;
            }else {
                // 3.若mysql存在 则还需将mysql数据回写到Redis
                redisTemplate.opsForValue().setIfAbsent(key,user,7L, TimeUnit.DAYS);
                return user;
            }
        }

        return user;
    }

2、双写一致性 业务代码逻辑实现(双检加锁)

    /**
     * Redis 双写一致性 业务逻辑代码
     * -------------------------- 大厂 高并发 加强版之双检加锁
     *
     * @param id
     * @return
     */
    @Override
    public User selectOneByIdPlus(Integer id) {
        User user = null;

        // 1.先查询Redis 若Redis存在 则直接返回
        String key = CHECK_USER_KEY + id;
        // 第一次检查
        user = (User) redisTemplate.opsForValue().get(key);

        // 2.若Redis不存在 则查询mysql 高并发情况下 需要加锁 以防止发生缓存击穿
        if (user == null){
            synchronized (UserServiceImpl.class){
                // 第二次检查 为什么要做第二次检查? 可能有多个线程同时执行了第一次检查,此时user均为null,到第二次检查,是为了防止多个线程同时写入Redis
                user = (User) redisTemplate.opsForValue().get(key);

                // 第二次从Redis查询仍为null 则去mysql查询
                if (user == null){
                    user = userDao.selectOne(id);
                    if (user == null) {
                        return null;
                    }else {
                        // 3.若mysql存在 则还需将mysql数据回写到Redis
                        redisTemplate.opsForValue().setIfAbsent(key,user,7L, TimeUnit.DAYS);
                        return user;
                    }
                }

            }
        }

        return user;
    }

3、Redis缓存一致性方案 

在分布式场景下,不可能做到强一致性原则,但可以实现最终一致性,即满足AP原则。

给缓存设置过期时间,定期清理缓存并回写,是保证数据最终一致性的解决方案。

        我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记,要以mysql的数据库写入库为准。

  • 先更新数据库,再更新缓存

更新缓存时,若Redis宕机,则会导致Redis数据丢失。

  • 先更新缓存,再更新数据库

不推荐,因为要以mysql业务库兜底。

  • 先删除缓存,再更新数据库

在高并发情况下,缓存删除成功但数据库更新中,有并发读请求,并发读请求从数据库读到旧值并回写到redis,导致后续都是从redis读取到旧值。

可以采用延迟双删策略。

例:A线程删除缓存成功后处于数据库更新中,此时B线程读数据,从mysql中读取并写回Redis,在A更新完数据库后,休眠一会,再对缓存进行一个二次删除,即延时双删。

(线程A休眠时间要大于线程B读取数据再写入缓存的时间)

  • 先更新数据库,再删除缓存

推荐使用。

4、Redis缓存一致性最终方案  

                       

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值