Redis缓存

发布时间:2024年01月21日

一 缓存基础

1 缓存的概念和作用

缓存就是数据交换的缓冲区(称作Cache),是存贮数据的临时地方,一般读写性能较高

2 缓存的使用

之前没有使用缓存是的模型

3 项目说明

# 具体实现流程
1 redis中查询商户缓存
2 判断是否存在
3 存在直接返回
4 不存在根据id去数据库查询
5 数据库也不存在,返回错误
6 存在则写入redis中
7 返回

二 数据一致性

1 思路

  • 查询数据的时候,如果缓存未命中,则查询数据库,将数据写入缓存设置超时时间

  • 修改数据时,先修改数据库,在删除缓存。

  • 延时双删策略

2 代码实现

  • 修改更新方法,添加超时时间

 @Override
 ? ?public Result queryById(Long id) {
 ? ? ? ?//1 redis中查询商户缓存
 ? ? ? ?String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
 ? ? ? ?//2 判断是否存在
 ? ? ? ?if(StrUtil.isNotBlank(shopJson)){
 ? ? ? ? ? ?//3存在直接返回
 ? ? ? ? ? ?Shop shop = JSONUtil.toBean(shopJson, Shop.class);
 ? ? ? ? ? ?return Result.ok(shop);
 ? ? ?  }
 ? ? ? ?//4 不存在根据id去数据库查询
 ? ? ? ?Shop shop = this.getById(id);
 ? ? ? ?//5 数据库也不存在,返回错误
 ? ? ? ?if(shop==null){
 ? ? ? ? ? ?return Result.fail("店铺不存在");
 ? ? ?  }
 ? ? ? ?//6 存在则写入redis中
 ? ? ? redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
 ? ? ? ?//7 返回
 ? ? ? ?return Result.ok(shop);
 ?  }

  • 修改ShopController

@PutMapping
????public Result updateShop(@RequestBody Shop shop) {
????????// 写入数据库
?
????????//shopService.updateById(shop);
????????//return Result.ok();
????????return ?shopService.update(shop);
??? }
  • 修改service代码 延时双删策略

 ?@Override
 ? ?public Result update(Shop shop) {
?
 ? ? ? ?Long id = shop.getId();
 ? ? ? ?if(id==null){
 ? ? ? ? ? ?return Result.fail("店铺id不存在");
 ? ? ?  }
 ? ? ? ? // 删除缓存
 ? ? ? ?redisTemplate.delete("cache.shop:" + id);
 ? ? ? ?// 更新数据库
 ? ? ? ?updateById(shop);
 ? ? ? ?Thread.sleep(800);
 ? ? ? ?// 删除缓存
 ? ? ? ?redisTemplate.delete("cache.shop:" + id);
 ? ? ? ?return Result.ok();
 ?  }

3 修改完代码以后,将所有的缓存删除,执行查询操作,多了超时


4 用postman执行修改方法: localhost:8081/shop

{
 ?"area":"大关",
 ?"sold":3035,
 ?"address":"金华路锦昌文华苑29号",
 ?"name":"102茶餐厅",
 ?"x":120.149192,
 ?"y":30.316078,
 ?"typeId":1,
 ?"id":1
}

执行完成以后,数据库的数据发生改变,查看redis的数据已经删除了。

三 缓存常见问题

1 缓存穿透:

客户端请求的数据,在数据库和redis中都不存在,这样缓存永远都不会生效,请求最终都到了数据库上。

解决方案:

  • 当在数据库查询的结果也不存在的时候,可以返回null值给redis,并且设置TTL

  • 布隆过滤器

布隆过滤器是一种数据结构,底层是位数组,通过将集合中的元素多次hash得到的结果保存到布隆过滤器中。主要作用就是可以快速判断一个元素是否在集合里面,但是因为算法的原因,也有一定概率的错误。

开发的时候我们一般选择空值值方式。

  • 代码方式实现

根据id查询的时候,如果信息不存在,则要将空值写入redis,并设置空值过期时间

@Override
 ? ?public Result queryById(Long id) {
 ? ? ? ?//1 redis中查询商户缓存
 ? ? ? ?String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
 ? ? ? ?//2 判断是否存在
 ? ? ? ?if(StrUtil.isNotBlank(shopJson)){
 ? ? ? ? ? ?//3存在直接返回
 ? ? ? ? ? ?Shop shop = JSONUtil.toBean(shopJson, Shop.class);
 ? ? ? ? ? ?return Result.ok(shop);
 ? ? ?  }
 ? ? ? ?if(shopJson!=null){
 ? ? ? ? ? ?return Result.fail("店铺不存在");
 ? ? ?  }
 ? ? ? ?//4 不存在根据id去数据库查询
 ? ? ? ?Shop shop = this.getById(id);
 ? ? ? ?//5 数据库也不存在,返回错误
 ? ? ? ?if(shop==null){
 ? ? ? ? ? ?// 空值写入redis中
 ? ? ? ? ? ?redisTemplate.opsForValue().set("cache.shop:" + id,"",3, TimeUnit.MINUTES);
 ? ? ? ? ? ?return Result.fail("店铺不存在");
 ? ? ?  }
 ? ? ? ?//6 存在则写入redis中
 ? ? ? redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
 ? ? ? ?//7 返回
 ? ? ? ?return Result.ok(shop);
 ?  }

2 缓存击穿:

也叫热点key问题,一个被高并发访问且业务复杂的key突然失效了,无数的请求瞬间给数据库带来的巨大冲击

解决方案: 互斥锁 逻辑过期


互斥锁思路:

查询缓存的时候,未命中需要获取锁 代码ShopServiceImpl

@Override
public Result queryById(Long id) {
 ? ?//1 redis中查询商户缓存
 ? ?String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
 ? ?//2 判断是否存在
 ? ?if(StrUtil.isNotBlank(shopJson)){
 ? ? ? ?//3存在直接返回
 ? ? ? ?Shop shop = JSONUtil.toBean(shopJson, Shop.class);
 ? ? ? ?return Result.ok(shop);
 ?  }
 ? ?if(shopJson!=null){
 ? ? ? ?return Result.fail("店铺不存在");
 ?  }
 ? ?Shop shop = null;
 ? ?String lockKey = "lock.id:" + id;
 ? ?try {
 ? ? ? ?//代码到这里说明没有命中缓存,那么就可以获取锁了
 ? ? ? ?boolean isLock = tryLock(lockKey);
 ? ? ? ?// 如果没有拿到锁,则等待一会,递归执行代码
 ? ? ? ?if(!isLock){
 ? ? ? ? ? ?Thread.sleep(100);
 ? ? ? ? ? ?queryById(id);
 ? ? ?  }
 ? ? ? ?//获取锁成功
 ? ? ? ?//4 不存在根据id去数据库查询
 ? ? ? ?shop = this.getById(id);
 ? ? ? ?//5 数据库也不存在,返回错误
 ? ? ? ?if(shop==null){
 ? ? ? ? ? ?// 空值写入redis中
 ? ? ? ? ? ?redisTemplate.opsForValue().set("cache.shop:" + id,"",3, TimeUnit.MINUTES);
 ? ? ? ? ? ?return Result.fail("店铺不存在");
 ? ? ?  }
 ? ? ? ?//6 存在则写入redis中
 ? ? ? ?redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
 ?  } catch (InterruptedException e) {
 ? ? ? ?e.printStackTrace();
 ?  } finally {
 ? ? ? ?unlock(lockKey);
 ?  }
 ? ?//7 返回
 ? ?return Result.ok(shop);
}
?
 ? ?// 获取锁
private boolean tryLock(String key){
 ? ?Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
 ? ?return BooleanUtil.isTrue(flag);
}
//释放锁
private void unlock(String key){
 ? ?redisTemplate.delete(key);
}

3 缓存雪崩

同一时间段内,大量的缓存key失效或者redis宕机,到时大量的请求到达数据库,带来巨大的压力。

解决方案

  • 给key设置随机的TTL

  • 集群方案防止宕机不可用

文章来源:https://blog.csdn.net/weixin_67366360/article/details/135692464
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。