当前位置: 首页 > news >正文

Redis数据缓存

缓存

一 缓存基础

1 缓存的概念和作用

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

2 缓存的使用

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

3 项目说明

当我们查询商家信息的时候,直接从mysql中获取的。现在我们将原来的项目改造。改造地方在ShopController,我们按照流程图去做,添加redis缓存,业务都是在service中实现的。

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

<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.47</version>
</dependency>

1 修改controller
/*** 根据id查询商铺信息* @param id 商铺id* @return 商铺详情数据
*/
@GetMapping("/{id}")
public Result queryShopById(@PathVariable("id") Long id) {return shopService.queryById(id);//        return Result.ok(shopService.getById(id));
}
2 修改service
@Resource
private RedisTemplate<String,Object> redisTemplate;
​
@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));//7 返回return Result.ok(shop);
}
3 改造首页

@Autowired
private RedisTemplate<String, Object> redisTemplate;
​
@Override
public Result queryTypeList() {
​List<Object> list = redisTemplate.opsForList().range("cache.list",0,-1);if(list!=null && list.size()!=0){return Result.ok(list);}List<ShopType> sort = this.query().orderByAsc("sort").list();if(sort==null||sort.size()==0){return Result.fail("列表不存在");}sort.forEach(s->redisTemplate.opsForList().rightPush("cache.list",s));return Result.ok(sort);
}

二 数据一致性

1 思路

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

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

  • 延时双删策略

2 代码实现

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

 @Overridepublic 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

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

 @Overridepublic 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,并设置空值过期时间

@Overridepublic 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

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

相关文章:

  • flutter的状态管理学习
  • vscode运行Python的两种方法,及无法运行的原因
  • Go语言实现各种hash算法
  • C //练习 4-4 在栈操作中添加几个命令,分别用在不弹出元素的情况下打印栈顶元素;复制栈顶元素;交换栈顶两个元素的值。另外增加一个命令用于清空栈。
  • ssm基于Javaweb的网上奶茶店系统的设计与实现论文
  • xtu-c语言考试复习-2
  • RAG(检索增强生成 )
  • vercel部署twikoo后评论收不到通知邮件问题解决方法
  • 飞桨分子动力学模拟-论文复现第六期:复现TorchMD
  • 奇怪的事情记录:外置网卡和外置显示器不兼容
  • Ceph入门到精通-通过 CloudBerry Explorer 管理对象bucket
  • ssh远程登陆
  • Kubernetes 集群管理—日志架构
  • iOS14 Widget 小组件调研
  • UniRepLKNet实战:使用 UniRepLKNet实现图像分类任务(二)
  • JS 中的深拷贝与浅拷贝
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • Git学习与使用心得(1)—— 初始化
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • Node 版本管理
  • Object.assign方法不能实现深复制
  • python学习笔记-类对象的信息
  • vue--为什么data属性必须是一个函数
  • webpack4 一点通
  • web标准化(下)
  • 工程优化暨babel升级小记
  • 力扣(LeetCode)965
  • 小程序 setData 学问多
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目
  • 《天龙八部3D》Unity技术方案揭秘
  • Unity3D - 异步加载游戏场景与异步加载游戏资源进度条 ...
  • 积累各种好的链接
  • ​一些不规范的GTID使用场景
  • #{}和${}的区别?
  • #HarmonyOS:软件安装window和mac预览Hello World
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (3)nginx 配置(nginx.conf)
  • (LeetCode 49)Anagrams
  • (MATLAB)第五章-矩阵运算
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (搬运以学习)flask 上下文的实现
  • (附源码)spring boot智能服药提醒app 毕业设计 102151
  • (附源码)springboot教学评价 毕业设计 641310
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (附源码)计算机毕业设计高校学生选课系统
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (源码版)2024美国大学生数学建模E题财产保险的可持续模型详解思路+具体代码季节性时序预测SARIMA天气预测建模
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .java 指数平滑_转载:二次指数平滑法求预测值的Java代码
  • .NET C#版本和.NET版本以及VS版本的对应关系
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .NET中的Event与Delegates,从Publisher到Subscriber的衔接!
  • // an array of int
  • /bin、/sbin、/usr/bin、/usr/sbin