Redis实战—商户查询缓存

本博客为个人学习笔记,学习网站:黑马程序员Redis入门到实战 实战篇之商户查询缓存

目录

什么是缓存

添加Redis缓存

缓存更新策略

数据库缓存不一致解决方案

案例:给查询商铺的缓存添加超时剔除和主动更新策略

缓存穿透

案例:实现缓存空对象方案 

缓存穿透小结 

 缓存雪崩

缓存击穿

基于互斥锁解决缓存击穿逻辑

基于逻辑过期解决缓存击穿逻辑

互斥锁与逻辑过期对比总结 

基于互斥锁方式解决缓存击穿问题

基于逻辑过期方式解决缓存击穿问题


什么是缓存

1. 缓存就是数据交换的缓冲区(称作Cache),是存贮数据的临时地方,一般读写性能较高。
2. 缓存数据存储于代码中,而代码运行在内存中,内存的读写性能远高于磁盘,因此缓存可以大大降低用户访问并发量带来的服务器读写压力。
3. 实际开发过程中,企业的数据量少则几十万,多则几千万,这么大数据量,如果没有缓存来作为"避震器",系统是几乎撑不住的,所以企业会大量运用到缓存技术。

但是缓存也会增加代码复杂度和运营的成本:
缓存的作用:
降低后端负载、提高读写效率,降低响应时间
缓存的成本:数据一致性成本:确保缓存和数据库中的数据是一致的


添加Redis缓存

原始的根据id查询商铺代码(直接访问数据库拿到商铺信息) 

/*** 根据id查询商铺信息* @param id 商铺id* @return 商铺详情数据*/
@GetMapping("/{id}")
public Result queryShopById(@PathVariable("id") Long id) {return Result.ok(shopService.getById(id));
}

现在我们需要对根据id查询商铺这段代码进行改造,为其添加Redis缓存

修改controller层代码

@GetMapping("/{id}")
public Result queryShopById(@PathVariable("id") Long id) {return shopService.queryById(id);
}

修改Service层代码

//Service接口类代码
Result queryById(Long id);//Service接口实现类代码
@Override
public Result queryById(Long id) {String key = "cache:shop:" + id;// 1.从redis查询商铺缓存String shopJson = stringRedisTemplate.opsForValue().get(key);// 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.若存在,将该商铺信息写入redisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop));// 7.返回return Result.ok(shop);
}

缓存更新策略

缓存更新是redis为了节约内存而设计出来的一个东西,主要是因为内存数据宝贵,当我们向redis插入太多数据,此时就可能会导致缓存中的数据过多,所以redis会对部分数据进行筛选更新,或者淘汰。


数据库缓存不一致解决方案

由于我们的缓存的数据源来自于数据库,而数据库的数据是会发生变化的,因此,如果当数据库中数据发生变化,而缓存却没有同步,此时就会有一致性问题存在。

解决缓存与数据库问题的方案有如下3种:(详细内容可参考视频P38黑马程序员Redis从入门到实战)
1. Cache Aside Pattern 人工编码方式:缓存调用者在更新完数据库后再去更新缓存,也称之为双写方案。
2. Read/Write Through Pattern : 缓存与数据库整合为一个服务由系统本身去维护一致性。调用者调用该服务,无需关心缓存一致性问题。
3. Write Behind Caching Pattern :调用者只操作缓存,由其它线程异步的将缓存数据持久化到数据库,保证最终一致。

综合考虑使用方案一,但是方案一调用者如何处理呢?这里有几个问题:
1. 当我们对数据库中的数据进行修改时,我们应该选择删除对应缓存还是更新对应缓存?
更新缓存:若每次更新数据库时都更新缓存,如果在一连串的更新中都没有对数据进行查询,那么只有最后一次更新是有意义的,中间的更新产生的无效写操作较多(X)
删除缓存:更新数据库时让删除缓存,等待下次查询时再把数据从数据库中更新到缓存  (✔)

2. 为确保缓存与数据库的数据一致性,应该如何保证缓存与数据库的操作同时成功或失败?
单体系统:将缓存与数据库操作放在一个事务
分布式系统:利用TCC等分布式事务方案

3. 我们应该选择先操作缓存还是先操作数据库?
先删除缓存,再操作数据库(X)
先操作数据库,再删除缓存  (✔)
解析(见下图,不要嫌麻烦,建议直接看视频理解更快 P38黑马程序员Redis从入门到实战):我们应选择先操作数据库,再删除缓存。原因在于,如果你选择第一种方案,在两个线程并发来访问时,假设线程1先来,它先把缓存删了,此时线程2过来,由于线程2查询缓存数据并不存在,此时线程2写入缓存,当线程2写入缓存后,线程1再执行更新动作时,实际上线程2写入缓存的就是旧数据,导致缓存中新数据被旧数据覆盖了。


案例:给查询商铺的缓存添加超时剔除和主动更新策略

需求1. 修改之前接口实现类中的部分代码,为其添加设置超时时间:如果在缓存中未找到所需数据,则从数据库查询,在将该数据填入缓存的同时为其设置超时时间。

stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);

需求2. 根据id修改店铺时,先修改数据库,再删除缓存

Controller层代码

    /*** 更新商铺信息** @param shop 商铺数据* @return 无*/@PutMappingpublic Result updateShop(@RequestBody Shop shop) {// 写入数据库return shopService.update(shop);}

Service层代码

//Service接口类方法
Result update(Shop shop);//Service接口实现类代码
@Override
@Transactional
public Result update(Shop shop) {if (shop.getId() == null) {return Result.fail("店铺id不能为空");}// 1.更新数据库updateById(shop);// 2.删除缓存stringRedisTemplate.delete("cache:shop:" + shop.getId());// 3.返回结果return Result.ok();
}

缓存穿透

案例:实现缓存空对象方案 


修改和新增查询商铺Service层接口实现类中的代码
1.代码修改: 在数据库查询不到该商铺信息时,向缓存中添加空值。


2. 代码新增:新增判断代码:当拿到缓存中的值,并判断该值为空(""或null)后,再次判断该值是否为空值(""),如果为空值,则说明该空值是先前在数据库查找不到店铺信息时向缓存写入的,间接说明数据库不存在该店铺信息,因此直接返回结果信息即可。


缓存穿透小结 

缓存穿透产生的原因是什么?
用户请求的数据在缓存中和数据库中都不存在,不断发起这样的请求给数据库带来巨大压力。

缓存穿透的解决方案有哪些?
1. 缓存null值
2. 布隆过滤
3. 增强id的复杂度,避免被猜测id规律,从而发起恶意请求
4. 做好数据的基础格式校验
5. 加强用户权限校验
6. 做好热点参数的限流


 缓存雪崩

缓存雪崩这部分内容视频里只做了介绍,没有给案例学习。 


缓存击穿

相比缓存雪崩是大量的key失效,缓存击穿指的是部分特殊的key失效带来的问题。缓存击穿问题也称作热点Key问题,就是一些被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

逻辑分析:假设线程1在查询缓存之后,本来应该去查询数据库,然后把这个数据重新加载到缓存的,此时只要线程1走完这个逻辑,其他线程就都能从缓存中加载这些数据了,但是假设在线程1没有走完的时候,后续的线程2,线程3,线程4同时过来访问当前这个方法, 那么这些线程都不能从缓存中查询到数据,那么他们就会同一时刻来访问查询缓存,都没查到,接着同一时间去访问数据库,同时的去执行数据库代码,对数据库访问压力过大

缓存击穿的常见解决方案有:
1.互斥锁  2.逻辑过期


基于互斥锁解决缓存击穿逻辑


因为锁能实现互斥性。假设线程过来,只能一个人一个人的来访问数据库,从而避免对于数据库访问压力过大,但这也会影响查询的性能,因为此时会让查询的性能从并行变成了串行,我们可以采用tryLock方法 + double check来解决这样的问题。

假设现在线程1过来访问,他查询缓存没有命中,但是此时他获得到了锁的资源,那么线程1就会一个人去执行逻辑,假设现在线程2过来,线程2在执行过程中,并没有获得到锁,那么线程2就可以进行到休眠,直到线程1把锁释放后,线程2获得到锁,然后再来执行逻辑,此时就能够从缓存中拿到数据了。


基于逻辑过期解决缓存击穿逻辑

我们之所以会出现这个缓存击穿问题,主要原因是在于我们对key设置了过期时间,假设我们不设置过期时间,数据不会失效,就不会有缓存击穿的问题。但是不设置过期时间,我们怎么能知道数据是否过期呢,因此我们选择采用逻辑过期方案。

我们把过期时间设置在 redis的value中,注意:这个过期时间并不会直接作用于redis,而是我们后续通过逻辑去处理。假设线程1去查询缓存,然后从value中判断出来当前的数据已经过期了,此时线程1去获得互斥锁,那么其他线程会进行阻塞,获得了锁的线程他会开启一个单独的线程去进行以前重构数据的逻辑,直到新开的线程完成这个逻辑后,才释放锁, 而线程1直接进行返回,假设现在线程3过来访问,由于线程线程2持有着锁,所以线程3无法获得锁,线程3也直接返回数据,只有等到新开的线程2把重建数据构建完后,其他线程才能走返回正确的数据。

这种方案巧妙在于,异步的构建缓存,缺点在于在构建完缓存之前,返回的都是脏数据。


互斥锁与逻辑过期对比总结 

互斥锁方案:由于保证了互斥性,所以数据一致,且实现简单,因为仅仅只需要加一把锁而已,也没其他的事情需要操心,所以没有额外的内存消耗,缺点在于有锁就有死锁问题的发生,且只能串行执行性能肯定受到影响。

逻辑过期方案:线程读取过程中不需要等待,性能好,有一个额外的线程持有锁去进行重构数据,但是在重构数据完成前,其他的线程只能返回之前的数据,且实现起来麻烦。


基于互斥锁方式解决缓存击穿问题

核心思路:相较于原来从缓存中查询不到数据后直接查询数据库而言,现在的方案是 进行查询之后,如果从缓存没有查询到数据,则进行互斥锁的获取,获取互斥锁后,判断是否获得到了锁,如果没有获得到,则休眠,过一会再进行尝试,直到获取到锁为止,才能进行查询

如果获取到了锁的线程,再去进行查询,查询后将数据写入redis,再释放锁,返回数据,利用互斥锁就能保证只有一个线程去执行操作数据库的逻辑,防止缓存击穿


基于逻辑过期方式解决缓存击穿问题

思路分析:当用户开始查询redis时,判断是否命中,如果没有命中则直接返回空数据,不查询数据库,而一旦命中后,将value取出,判断value中的过期时间是否满足,如果没有过期,则直接返回redis中的数据,如果过期,则在开启独立线程后直接返回之前的数据,独立线程去重构数据,重构完成后释放互斥锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/725713.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

奇富科技:大数据任务从诊断到自愈的实践之路

一、为什么要做诊断引擎 毓数平台是奇富科技公司自主研发的一站式大数据管理、开发、分析平台,覆盖大数据资产管理、数据开发及任务调度、自助分析及可视化、统一指标管理等多个数据生命周期流程,让用户使用数据的同时,挖掘数据最大的价值。…

打造高效、安全的交易平台:开发流程与关键要素解析

在数字化时代,大宗商品交易平台开发/搭建已成为连接买家与卖家的桥梁,为无数企业和个人提供了便捷、高效的交易机会。然而,随着市场的竞争日益激烈,如何打造一个既符合用户需求又具备竞争力的交易平台,成为了众多开发者…

文件上传{session文件包含以及条件竞争、图片文件渲染绕过(gif、png、jpg)}

session文件包含以及条件竞争 条件: 知道session文件存储在哪里 一般的默认位置: /var/lib/php/sess_PHPSESSID /var/lib/php/sessions/sess_PHPSESSID /tmp/sess_PHPSESSID /tmp/sessions/sess_PHPSESSID ####在没做过设置的情况下一般都是存储在/var…

解决WordPress更新插件或者更新版本报WordPress 需要访问您网页服务器的权限的问题

文章目录 前言一、原因二、解决步骤总结 前言 当对WordPress的插件或者版本进行更新时报错:要执行请求的操作,WordPress 需要访问您网页服务器的权限。 请输入您的 FTP 登录凭据以继续。 如果您忘记了您的登录凭据(如用户名、密码&#xff09…

光线追踪7 - 抗锯齿(Antialiasing)

目前为止,如果你放大渲染出的图像,可能会注意到图像边缘的明显“阶梯状”效果。这种阶梯效果通常被称为“走样”或“锯齿”。当真实相机拍摄图片时,边缘通常没有锯齿,因为边缘像素是一些前景和一些背景的混合。请考虑,…

5. 链接和加载(linker and loader)

链接和加载(linker and loader): linker即链接器,它负责将多个.c编译生成的.o文件,链接成一个可执行文件或者是库文件; loader即加载器,它原本的功能很单一只是将可执行文件的段拷贝到编译确定的内存地址即可&#x…

英福康INFICON残余气体RGA General Chinese中文培训PPT课件

英福康INFICON残余气体RGA General Chinese中文培训PPT课件

【树上倍增】【割点】 【换根法】3067. 在带权树网络中统计可连接服务器对数目

作者推荐 视频算法专题 本文涉及知识点 树上倍增 树 图论 并集查找 换根法 深度优先 割点 LeetCode3067. 在带权树网络中统计可连接服务器对数目 给你一棵无根带权树,树中总共有 n 个节点,分别表示 n 个服务器,服务器从 0 到 n - 1 编号…

Java | 在消息对话框中显示文本

首先需要导入JOptionPane类,JOptionPane类属于Swing组件中的一种,其导入方式如下: import javax.swing.JOptionPane;可以使用JOptionPane的showMessageDialog方法显示消息文本。 参数格式: JOptionPane.showMessageDialog(paren…

【C语言】指针详细解读2

1.const 修饰指针 1.1 const修饰变量 变量是可以修改的,如果把变量的地址交给⼀个指针变量,通过指针变量的也可以修改这个变量。 但是如果我们希望⼀个变量加上⼀些限制,不能被修改,怎么做呢?这就是const的作⽤。 …

RK3568平台开发系列讲解(基础篇)注册字符设备

🚀返回专栏总目录 文章目录 一、字符设备初始化二、字符设备的注册和注销三、实验代码沉淀、分享、成长,让自己和他人都能有所收获!😄 注册字符设备可以分为两个步骤: 字符设备初始化字符设备的添加一、字符设备初始化 字符设备初始化所用到的函数为 cdev_init(…),在对…

解决QMYSQL driver not loaded问题

前言 之前都是在Qt5.51上开发,连接mysql数据库一直没有问题,换到5.15.2后一直报错 一查才发现\5.15.2\msvc2019_64\plugins\sqldrivers目录下没有qsqlmysql了,5.5.1是有的,5.15.2是要自己编译的。。。 下载源码 安装qt的时候没…

查看kafka消息消费堆积情况

查看主题命令 展示topic列表 ./kafka-topics.sh --list --zookeeper zookeeper_ip:2181描述topic ./kafka-topics.sh --describe --zookeeper zookeeper_ip:2181 --topic topic_name查看topic某分区偏移量最大(小)值 ./kafka-run-class.sh kafka.too…

旧物回收小程序开发:环保与科技的创新结合

随着科技的飞速发展,我们的日常生活越来越离不开手机应用程序。而在环保日益成为社会焦点的今天,如何将科技与环保相结合,成为了一个值得深思的问题。今天,我们将探讨旧物回收小程序的开发,它如何助力环保,…

【重要公告】BSV区块链上线TypeScript SDK,未来将支持更多开发语言

​​发表时间:2024年2月21日 BSV区块链协会宣布上线JavaScript和TypeScript SDK(即“标准开发工具包”)。TypeScript SDK旨在为开发者提供新版统一核心代码库,以便利开发者在BSV区块链上开发能够任意扩容的应用程序。新上线的SDK替…

掌握Python操作Word:从基础到高级全覆盖

掌握Python操作Word:从基础到高级全覆盖 引言Python操作Word的基础文档的创建与打开文档的基本操作 创建和打开Word文档创建新的Word文档打开现有文档读取文档内容修改现有文档 编辑文档内容添加和编辑文本设置格式插入标题 处理文档结构操作段落列表的处理表格的操…

深入理解 Vuex:从基础到应用场景

前言 在之前的文章中,我们已经对 Vue.js 有了一定的了解。今天我们要对Vue官方的状态共享管理器Vuex进行详细讲解,将其基本吃透,目标是面对大多数业务需求; 一、介绍 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用…

『操作系统OS笔记』MAC(m1芯片)电脑安装FFmpeg

MAC(m1芯片)电脑安装FFmpeg mac电脑安装ffmpeg两种方法 文章目录 1. brew安装FFmpeg2. 官网下载FFmpeg压缩包3. 使用FFmpeg将音频和视频合并 1. brew安装FFmpeg brew install ffmpeg # 需要等比较久的时间,安装很多东西,安装过程中如果遇到报错对应解决…

MES数据采集设备

在智能制造日益盛行的今天,MES(制造执行系统)作为连接计划与生产现场的关键环节,其重要性不言而喻。而MES数据采集设备则是MES系统的核心组件,负责实时、准确地获取生产现场的各种数据,为企业的生产决策提供…

信息系统项目管理师--范围管理

项⽬范围管理 产品范围:指某项产品、服务或成果所具有的特征和功能。产品范围的完成情况是根据产品需求来衡量的。“需求”是指根据特定协议或其他强制性规范,产品、服务或成果 必须具备的条件或能⼒。 项⽬范围:包括产品范围,是为…