Redis( 缓存篇 ==> 超详细的缓存介绍与数据一致性解决方案 & 代码实现

本章导学:

  • 了解什么是缓存
  • 了解缓存更新的几种策略
    • 内存淘汰
    • 过期剔除
    • 主动更新(重点掌握)
  • 代码实现

一、什么是缓存?

缓存就是数据交换的缓冲区,是存贮数据的临时地方,一般读写速度性能较高

当今电脑的CPU发展已经非常强大了,它的运算能力十分之高,已经远远超出内存、磁盘的读写数据的能力。但是,CPU所做的任何运算,都要先从磁盘或者内存读出数据,放到CPU寄存器里后才可以进行运算。所以就出现了一个问题: 数据读写能力远远低于CPU运算能力,计算机性能收到了限制

那么为了解决这个问题,人们就在CPU内部添加了一块缓存区,CPU会把经常需要读写的数据,放到CPU缓存区,当我们做高速运算的时候,就不用从内存或者磁盘里读出数据,而是直接从CPU内部的缓存区取出数据,大大提高了读取速度。

aeb13db2d0814cf58831c8bd6d7871d1.png

但是凡事有利就有弊,缓存也是如此。如果我们从磁盘里读出了数据并添加到缓存,然后磁盘里的数据进行了变更,但我们缓存区的数据没有随之更新,就出现了数据一致性问题。

优势:

  • 降低了后端负载
  • 提高了读写效率
  • 降低相应时间

弊端:

  • 数据一致性问题
  • 代码维护成本提高

 

二、缓存更新的策略

为了解决数据一致性问题,我们需要更新缓存。这里介绍几种更新缓存的策略。

2.1、内存淘汰策略

说明:开发人员无需自己维护,利用Redis自带的缓存淘汰机制,当内存不足时自动淘汰部分缓存,下次查询时再更新缓存。

一致性:低。因为该机制只会在内存不足时触发。当且仅当内存不足时,才会删除掉旧的缓存,待下次查询时进行缓存更新。

维护成本:无。使用Redis缓存淘汰机制,开发人员无需任何操作。


2.2、超时剔除策略

说明:在添加缓存的时候,主动为缓存设置过期时间(TTL),待设置时间结束后缓存自动删除,等待下次查询后更新缓存。

一致性:一般。取决于设置的过期时间长短。

维护成本:低。只需要开发人员在添加缓存时设置TTL即可。


2.3、主动更新策略

 说明:在开发人员编写业务逻辑时,只要遇到数据库的修改,同步更新缓存。

一致性:高。数据库一变更,缓存直接

维护成本:高。需要开发人员编码实现数据库与缓存同步。


具体选择哪种方案来解决数据一致性问题,我们需要结合业务场景来看。

低一致性需求:采用内存淘汰策略

高一致性需求:采用主动更新策略,并使用过期淘汰策略作为兜底。

在采用主动更新策略的时候,我们还需要考虑三个问题:

1、是更新缓存还是删除缓存?

2、如何保证数据库与缓存同成功或同失败?

3、是先操作数据库还是先操作缓存?

 

问题一:先删除缓存较好。假设我们每次更新数据库都对应的更新缓存,那么如果该数据修改了一万次,我们也要更新一万次缓存,而用户只访问该数据一次,无效的<写>操作太多了! 

而如果我们先删除缓存,不论数据库修改了多少次,只有当用户访问了这条数据,我们才把数据添加到缓存里,那么对于缓存的操作只有一次。

问题二:把缓存与数据库操作放在同一个事务里即可。

问题三:我们看下面的情况演示

             先删除缓存,再更新数据库正常情况),数据库原始数据V1=10

d20a658e331e414f917039537214eff8.png

             先删除缓存,再更新数据库异常情况),数据库原始数据V1=10

 089fe7b41cc8407fa69394d7cbe79de4.png

接下来我们看先更新数据库,再删除缓存的两种情况 

            先更新数据库,再删除正常情况),数据库原始数据V1=10

f0d2866b7e5340428288831a4a445c5c.png

             先更新数据库,再删除异常情况),数据库原始数据V1=10

5d1a42b176eb4b8b8b3e57c5c1e11258.png

我们主要分析一下这种情况:首先,要达成上述情况我们需要满足两个前提:

1,再用户查询的时候,缓存恰好过期了

2,再查询完数据库写入缓存的这微秒时间(缓存写入是非常快的),被线程B抢占更新了数据库,而数据库的更新往往是比较慢的。只有非常非常小的概率会出现,在缓存写入过程中的微秒时间内,数据库完成了更新并删除了缓存两个步骤

总结:

基于以上四种分析,我们可以得出之前提过三个问题的答案了。

1、是更新缓存还是删除缓存?

答:删除缓存

2、如何保证数据库与缓存同成功或同失败?

答:把数据库与缓存操作放在同一个事务

3、是先操作数据库还是先操作缓存?

答:先操作数据库

所以更新缓存的最佳策略就是:

低一致性需求:使用Redis内存淘汰策略

高一致性需求:使用主动更新策略,并用过期淘汰策略兜底

读操作:

  • 缓存命中直接返回
  • 缓存未命中,查询数据库后,写入缓存,并设置过期时间

写操作:

  • 先修改数据库,再删除缓存

三、代码实现

需求:通过ID查询店铺信息,并添加到缓存。需要保证数据一致性

操作:先从Redis查询缓存,命中了直接返回,没命中去数据库查询,查询后添加缓存并设置过期时间。

Controller层方法如下:

c5a488ecbe4045d6891faa37f19aa850.png

具体实现: 

    //需求,通过ID查询店铺信息,并添加到缓存。需要保证数据一致性
    @Override
    public Result queryById(Long id) {
        //定义个cacheID作为key,后续方便使用
        String cacheId = CACHE_SHOP_KEY + id;
        
        //1、从Redis中查询商户信息
        String shopJson = stringRedisTemplate.opsForValue().get(cacheId);

        //2、击中了,转成对象直接返回
        if(StrUtil.isNotBlank(shopJson)){
            return Result.ok(JSONUtil.toBean(shopJson,Shop.class));
        }

        //3、没击中,去数据库查
        //Shop shop = getById(id);
        Shop shop = query().eq("id", id).one();

        //4、数据库里也没查到,返回异常
        if(shop == null){
            return Result.fail(SHOP_ERROR);
        }

        //5、添加数据到redis缓存
        stringRedisTemplate.opsForValue().set(cacheId,JSONUtil.toJsonStr(shop));
        
        //6、设置过期时间,30分钟
        stringRedisTemplate.expire(cacheId,CACHE_SHOP_TTL, TimeUnit.SECONDS);

        //6、返回数据
        return Result.ok(shop);
        
    }

需求:通过ID修改店铺信息,并添加到缓存。需要保证数据一致性

操作:先更新数据库信息,再删除缓存(更新里只用做这两步),待下一次查询后,根据查询到的数据库信息添加缓存,并设置过期时间

Controller层方法如下:

e72d8d710b2347f193a3fab37ad7a63c.png

 具体实现:

    //数据库与缓存的操作放在同一个事务中处理
    @Override
    @Transactional
    public Result updateByShopId(Shop shop) {
        //判断用户输入的ID是否存在
        Long shopId = shop.getId();
        if(shopId == null){
            return Result.fail("商铺ID不存在!");
        }

        //1、更新数据库
        boolean update = updateById(shop);

        //2、删除缓存
        //key的话我们在查询方法里有定义过的         String cacheId = CACHE_SHOP_KEY + id;
        stringRedisTemplate.delete(CACHE_SHOP_KEY + shop.getId());

        return Result.ok();
    }

 接下来我们访问一下商铺信息,观察Redis缓存

7ab2ffeac4744cf790b61214edb85ced.png

可以发现,缓存已经成功添加了。

我们再测试一下执行修改操作后,会不会把缓存成功删除

用POSTMAN发个请求

20e9f9db01ba40c78f01fd13fb994c59.png

去Redis看看缓存删掉没

f56ea2a2a83b4909aab1f31897aa9085.png

成功删除!

我们重新访问店铺信息

a60f863bf477498b8e82d0409d082b10.png

b8d0fde0e1df44ce91d73e458b3a42e9.png

 

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白日日白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值