redis实战-商户查询缓存

一、缓存的概念

1.1 概念

缓存就是数据交互的缓冲区(称为Cache),是存储数据的临时地方,一般读写性较高。
在这里插入图片描述

1.2 代码实现

在这里插入图片描述

    @GetMapping("/{id}")
    public Result queryShopById(@PathVariable("id") Long id) {
   
   

        return shopService.queryById(id);
    }

在这里插入图片描述

public interface IShopService extends IService<Shop> {
   
   

    Result queryById(Long id);
}

在这里插入图片描述

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
   
   

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public Result queryById(Long id) {
   
   
        //1.从redis查询商户缓存
        String shopJson=stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY+id);
        //2.判断是否存在
        if(StrUtil.isNotBlank(shopJson)){
   
   
            //3.存在,直接返回
            Shop shop = JSONUtil.toBean(shopJson,Shop.class);
            return Result.ok(shop);
        }
        //4.不存在,根据id查询数据库
        Shop shop = getById(id);
        //5.不存在,返回错误
        if(shop == null){
   
   
            return Result.fail("店铺不存在!");
        }
        //6.存在,写入redis
        stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(shop));

        return Result.ok(shop);
    }
}

启动服务测试:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
第二次访问:
在这里插入图片描述
另外,可以通过DEBUG日志来查看是否执行了数据库查询操作

二、缓存更新策略

2.1 三种缓存更新策略

缓存存在数据一致性问题,如数据库里的数据改变,缓存中的数据未及时更新。这需要在代码中解决该问题。
在这里插入图片描述
对于低一致性需求的数据可以选择“内存淘汰”和“超时剔除”策略,对于高一致性需求的数据可以选择“主动更新”策略,并以“超时剔除”为兜底方案。

2.2 主动更新策略(主流)

2.2.1 具体内容

主动更新策略又可以有以下三种实现方式:
在这里插入图片描述
企业中使用的最多的是01Cache Aside Pattern。
在这里插入图片描述
(1)选删除缓存的原因:若是有多次更新操作但是无查询,一直去更新缓存造成无效写操作,此时直接删除缓存,等后面有了查询再添加缓存的机制更佳。
(2)对分布式事务感兴趣可以去了解springcloud等微服务的知识。
(3)先删缓存还是先操作数据库的两种情况分析:
上图中的分布式事务的使用场景是两个业务在不同的机器上执行,如一台服务器更新数据库后利用mq机制让另一台服务器去完成删除缓存的操作,而为了保证操作的完整性,需要利用分布式事务的机制。但会存在一些问题。

2.2.2 问题

2.2.2.1 先删缓存再操作数据库

先删缓存再操作数据库的正常流程:
在这里插入图片描述
异常情况:
在这里插入图片描述
上图会导致缓存中的数据与数据库中的数据不一致。

2.2.2.2 先操作数据库再删除缓存

正常情况:
在这里插入图片描述
异常情况:
在这里插入图片描述
“先操作数据库再删除缓存”的异常发生的概率极低(更新数据库操作比读缓存还快),所以建议先更新数据库再删缓存的方案。

2.2.2.3 最终方案

在这里插入图片描述

2.2.3 代码实现

在这里插入图片描述
写操作换成主动更新策略:
在这里插入图片描述

    @PutMapping
    public Result updateShop(@RequestBody Shop shop) {
   
   
        // 写入数据库
        return  shopService.update(shop);
    }

在这里插入图片描述

public interface IShopService extends IService<Shop> {
   
   

    Result queryById(Long id);

    Result update(Shop shop);
}

(使用@Transactional保证原子性):
在这里插入图片描述

    @Override
    @Transactional
    public Result update(Shop shop) {
   
   
        Long id = shop.getId();
        if(id==null){
   
   
            return Result.fail("店铺id不能为空");
        }
        //1.更新数据库(使用MybatisPlus写好的方法)
        updateById(shop);
        //2.删除缓存
        stringRedisTemplate.delete(CACHE_SHOP_KEY+id);
        return Result.ok();
    }

启动服务验证:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、缓存穿透

3.1 概念

缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打倒数据库。

3.2 解决方案

3.2.1 缓存空对象

在这里插入图片描述
在这里插入图片描述
造成短期不一致的原因:没查到却保留了一个null缓存,后续插入一条数据后,在null缓存还未清楚前用户查到的还是null缓存。

3.2.2 布隆过滤

在这里插入图片描述
新增一个“布隆过滤器”,其他流程不变。布隆过滤器里并不是存储了数据库中的所有数据,而是有一个map数组,里面存的是二进制位,当要判断数据库中的数据是否存在时,并不是真的把数据存在布隆过滤器,而是把这些数据基于某种哈希算法计算出哈希值,然后将哈希值转换成二进制位保存到布隆过滤器中。注意,布隆过滤器是概率统计。当布隆过滤器返回不存在时那数据库必定不存在;当布隆过滤器返回存在时那数据库中不一定存在,也有一定的穿透风险。
在这里插入图片描述

3.3 代码实现

3.3.1 缓存空对象

方案调整如下:
在这里插入图片描述
修改查询店铺信息的代码:
在这里插入图片描述

@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
   
   

    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public Result queryById(Long id) {
   
   
        //1.从redis查询商户缓存
        String shopJson=stringRedisTemplate.opsForValue
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值