Redis缓存实战(2)

命运对每个人都是一样的,不一样的是各自的努力和付出不同,付出的越多,努力的越多,得到的回报也越多,在你累的时候请看一下身边比你成功却还比你更努力的人,这样,你就会更有动力。

导读:本篇文章讲解 Redis缓存实战(2),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

目录

 

缓存定义

Redis缓存实战

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

2如何保证缓存与数据库的操作同时成功或者失败?

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

缓存问题

缓存穿透

缓存雪崩

缓存击穿


 

缓存定义

缓存(Cache)是数据交换的缓冲区,是存储数据的临时地方,读写性能较高。缓存的作用:降低后端负载并且提高读写时间,降低响应时间。缓存的成本:数据一致性成本,代码维护成本和运维成本。

Redis缓存实战

此代码模拟商户查询,查询商户时首先从redis缓存中查询是否有符合自己id的商户,如果有直接返回,如果没有然后从数据库中查询,如果数据库中查询到对应的数据,然后将该数据添加到redis缓存中,并且返回该数据,如果还是没有查询到就会返回异常信息。代码如下

   @GetMapping("/select")
    public Result select(Long id){
        TbShop tbShop = (TbShop) redisTemplate.opsForValue().get("shopcache" + id);
        if(tbShop!=null){
            return Result.success(tbShop);
        }
        TbShop tbShop1 = tbShopMapper.selectById(id);
        if(tbShop1==null){
            return Result.error("404","未发现该商铺");
        }
        redisTemplate.opsForValue().set("shopcache"+id,tbShop1,30,TimeUnit.MINUTES);
        return Result.success(tbShop1);

    }

对于更新数据库信息,缓存更新策略如下图

fa5ad124c1c846bc923bb46450b7b589.png

 这里采用主动更新策略,在更新数据库的时候同时更新缓存。操作缓存和数据库有三个问题需要考虑如下。

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

更新缓存:每次更新数据库都更新缓存,无效操作比较多

删除缓存:更新数据库让缓存失效,查询时更新缓存,效果比较好

2如何保证缓存与数据库的操作同时成功或者失败?

单体系统:将缓存和数据库放在一个事务中。

分布式系统:利用TCC等分布式事务方案。

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

先删除数据库再操作数据库会遇到如下问题

f12e94531f994e13960a47d813615398.png

 经过如图所示过程就会出现缓存保存旧的数据的情况。并且这种情况的发生概念是非常高的。

先操作数据库,再删除缓存会出现如下情况

75231944090542ee955578a7871ab357.png

 这种情况下也会发生缓存中存入旧数据的情况,但是由于缓存的执行时间远远小于数据库操作数据,所有这种情况的发生概念会比较小,因此可以选择先更新数据库,然后再删除缓存。

代码如下

   @Transactional
    @PostMapping("/update")
    public Result update(@RequestBody  TbShop tbShop){
        int update = tbShopMapper.updateById(tbShop);
        redisTemplate.delete("shopcache"+tbShop.getId());
        return Result.success();

    }

缓存问题

缓存穿透

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

常见的解决方案:缓存空对象或者布隆过滤。

缓存空对象优点:实现简单,维护方便,缺点:额外的内存消耗,可能造成短期的不一致。

布隆过滤:优点:内存占用少,没有多余的key。缺点:实现复杂和存在误判可能。

 

缓存雪崩

缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

解决方案

给不同的Key的TTL添加随机值

利用Redis集群提高服务的可用性

给缓存业务添加降级限流策略

给业务添加多级缓存

 

缓存击穿

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

解决方案:可以通过互斥锁和逻辑过期的方式进行解决。

fc7949cff5684ef1b5a46a0c951ecd07.png

 这里利用互斥锁的方式来进行模拟

互斥锁流程如下图

8c7fc1478cca4ce3acaefa5942ff79a1.png

 代码如下

 @GetMapping("/select1")
    public Result select1(Long id){
        TbShop tbShop = (TbShop) redisTemplate.opsForValue().get("shopcache" + id);
        if(tbShop!=null){
            return Result.success(tbShop);
        }
        TbShop tbShop1 = null;
        String lockkey = "lockshop"+id;
        try {
            boolean trylock = trylock(lockkey);
            if(!trylock){
                Thread.sleep(50);
               return select(id);
            }
            tbShop1 = tbShopMapper.selectById(id);
            System.out.println("数据库查询");
            Thread.sleep(200);
            if(tbShop1==null){
                return Result.error("404","未发现该商铺");
            }
            redisTemplate.opsForValue().set("shopcache"+id,tbShop1,30,TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            unlock(lockkey);
        }
        return Result.success(tbShop1);

    }

  private boolean trylock(String key){
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(key, "1", 10, 
        TimeUnit.SECONDS);
        return BooleanUtil.isTrue(aBoolean);
    }
    private void unlock(String key){
        redisTemplate.delete(key);
    }

 

通过jmeter压测工具使用一千多个线程进行测试,后端仅仅只是执行了一次调用数据库操作,实验结果因此得到验证。

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/143038.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!