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

redis进阶:mysql,redis双写一致性,数据库更新后再删除缓存就够了吗?

0. 引言

最近线上的一个状态修改功能出现了问题,一开始是运营找了过来,运营告知某条数据的状态已经开启了的,但是实际使用起来还是没有生效,于是拿到这个问题后,首先就去数据库查了这条数据,发现确实如他所说,状态数据是已经更改过的。但是为什么没有生效呢?

于是再次查看了获取数据的方法,发现是优先获取的缓存,于是查询缓存里的数据,发现了问题所在,缓存里并没有将这条数据的状态更改过来,也就是缓存数据不一致问题。

继续追查状态修改的方法,发现采用的更新方式是先更新数据库,然后删除缓存。这是我们常用的双写一致性的处理方法,但也正是这样的方式出现了问题。下面我们来详细讲解

1. 先更新数据库,再删除缓存为什么有问题?

首先原来的方法,大概是这样的,将数据库更新后,再将缓存删除掉

    public R save(UserVO userVO) {User user = new User();BeanUtils.copyProperties(userVO, user);saveUser(user);redisTemplate.delete("userInfo:" + user.getUserName());return R.success("操作成功");}

同时在获取数据时,采用的是先从缓存获取,如果缓存没有,就从数据库查询,并同时存放到缓存上,这样保证了下次访问时数据能直接从缓存获取,减少了数据库压力

    public User getByUserName(String userName) {User user = (User) redisTemplate.opsForValue().get(userName);if (user != null) {return user;}user = this.getOne(Wrappers.<User>lambdaQuery().eq(User::getUserName, userName));redisTemplate.opsForValue().set("userInfo:" + userName, user);return user;}

数据库的数据已经更新成功了,说明数据库是没有报错了,那么哪个地方报错了呢?当然是redis,在执行redis的删除操作时,如果发现错误,比如因为网络问题,或者redis本身服务问题,导致没有正常执行而产生的报错,所以为了捕获这个报错,我们需要添加上数据库事务 @Transactional(rollbackFor = Exception.class)

    @Transactional(rollbackFor = Exception.class)public R save(UserVO userVO) {User user = new User();BeanUtils.copyProperties(userVO, user);saveUser(user);redisTemplate.delete("userInfo:" + user.getUserName());return R.success("操作成功");}

但是这样你以为就结束了吗,这样的操作确实可以实现事务的回滚,但是上述的模式仅仅只是我们用到的Cache Aside模式(这里大家要了解redis缓存的四种模式),而对于另外一种同样经典的Write Through模式就不再适用于这个操作了。

2. Cache Aside模式与Write Through模式的区别

Cache Aside模式采用的是读时放缓存或者预加载缓存,写时更新数据库,删缓存,它适用于读多写少,因为缓存没有时要去数据库查数据,如果多线程场景下,同一时间打进来同样的请求,都会去访问数据库,一方面容易造成缓存击穿,一方面并不能保证数据的强一致性,比如如下场景:

两个请求同时执行:
请求A查询用户A信息,请求B更新用户A信息
请求A查询缓存没有值,于是去查询数据库,获取到值为old(还没有更新缓存)
请求B更新数据库用户A信息为new
请求B删除掉用户A信息的缓存
请求A写入缓存,值为old
至此,就导致用户A的数据缓存为旧数据了

Write Through模式采用的是读缓存,写时先更新缓存,再更新数据库,一般缓存不设置过期时间,适用于频繁查询缓存数据的场景。因为是先更新缓存,再更新数据库,且在查询操作时不做更新缓存,所以保证了数据的一致性,也防止了缓存击穿。

3. Write Through模式下的双写一致性

因为是先更新缓存,再更新数据库,那么更新方法的代码示例就变成了如下所示:

    @Transactional(rollbackFor = Exception.class)public R save3(UserVO userVO) {User user = new User();// 拷贝属性BeanUtils.copyProperties(userVO, user);redisTemplate.opsForValue().set(user.getUserName(), user);userMapper.addUser(user);return R.success("操作成功");}

但是这样的操作,肯定是有问题的,因为一旦数据库报错,@Transactional能够保证数据库回滚,但并不能保证redis的事务性,于是我们需要让redis也能保证事务

方案一: SessionCallback实现redis事务

以为redis本身就自带事务指令,所以最容易想到的就是通过事务指令来实现redis事务,结合redisTemplate实现,需要借助SessionCallback回调类,实现代码如下

   @Transactional(rollbackFor = Exception.class)public R save2(UserVO userVO) {// 本地事务+redis事务 = 双写一致性/缓存强一致性/redis.mysql事务回滚User user = new User();// 拷贝属性BeanUtils.copyProperties(userVO, user);Object execute = redisTemplate.execute(new SessionCallback<List<Boolean>>() {@Overridepublic List<Boolean> execute(RedisOperations operations) throws DataAccessException {// 事务开启operations.multi();// 执行的操作,redis先操作operations.opsForValue().set(user.getUserName(), user);try {saveUser(user);} catch (Exception e) {// 事务取消operations.discard();e.printStackTrace();return null;}// 事务的提交return operations.exec();}});return R.status(execute != null);}

方案二: setEnableTransactionSupport实现redis事务

这样的代码还是比较长的,书写起来略显麻烦,能不能有更简单的方法,或者说能不能让redis也适配 @Transactional注解,实现事务操作。答案是当然可以的,在redisTemplate中有这样的一个方法setEnableTransactionSupport(true),它可以开启redis支持数据库事务

@Configuration
public class RedisConfig {/*** 创建RedisTemplate* @param redisConnectionFactory* @return*/@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){// 创建对象RedisTemplate redisTemplate = new RedisTemplate();// 设置连接工厂redisTemplate.setConnectionFactory(redisConnectionFactory);// 设置redis生成的key的序列化器,对key编码进行处理RedisSerializer stringSerializer = new StringRedisSerializer();redisTemplate.setKeySerializer(stringSerializer);redisTemplate.setHashKeySerializer(stringSerializer);// 设置redis支持数据库事务redisTemplate.setEnableTransactionSupport(true);return redisTemplate;}
}

开启之后,我们的更新代码就变成了如下所示,非常的简洁清爽

    @Transactional(rollbackFor = Exception.class)public R save3(UserVO userVO) {User user = new User();// 拷贝属性BeanUtils.copyProperties(userVO, user);redisTemplate.opsForValue().set(user.getUserName(), user);userMapper.addUser(user);return R.success("操作成功");}

测试

最后我们来进行一个模拟测试,我们将sql中的字段故意写成不存在的字段名

在这里插入图片描述

请求这个接口

在这里插入图片描述

报错字段password2不存在

在这里插入图片描述
查看数据库中并没有添加成功

在这里插入图片描述

缓存里同样也没有,说明我们的redis支持了数据库的事物操作

在这里插入图片描述

相关文章:

redis进阶:mysql,redis双写一致性,数据库更新后再删除缓存就够了吗?

0. 引言 最近线上的一个状态修改功能出现了问题&#xff0c;一开始是运营找了过来&#xff0c;运营告知某条数据的状态已经开启了的&#xff0c;但是实际使用起来还是没有生效&#xff0c;于是拿到这个问题后&#xff0c;首先就去数据库查了这条数据&#xff0c;发现确实如他所…...

RTOS中互斥量的原理以及应用

互斥量的原理 RTOS中的互斥量是一种同步机制&#xff0c;用于保护共享资源&#xff0c;防止多个任务同时访问该资源&#xff0c;从而避免数据竞争和不一致性。 互斥量的原理是通过对共享资源进行加锁和解锁操作来实现的。 在RTOS中&#xff0c;互斥量通常是一个数据结构&…...

数据分析:基于随机森林(RFC)对酒店预订分析预测

数据分析&#xff1a;基于随机森林(RFC)对酒店预订分析预测 作者&#xff1a;AOAIYI 作者简介&#xff1a;Python领域新星作者、多项比赛获奖者&#xff1a;AOAIYI首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x1f…...

【python】序列(列表、元组)、字典、集合的初步认识

一、序列 序列类型(sequence)&#xff1a;一组有序的数据集&#xff0c;特点是数据之间存在先后关系&#xff0c;通过序号访问 序列包含以下三种类型&#xff1a; 1.字符串&#xff08;str&#xff09;不可修改 2.列表&#xff08;list&#xff09;可修改 3.元组&#xff08;t…...

周赛335(模拟、质因子分解、分组背包)

题解&#xff1a;0x3f https://leetcode.cn/problems/number-of-ways-to-earn-points/solution/fen-zu-bei-bao-pythonjavacgo-by-endlessc-ludl/ 文章目录周赛335[6307. 递枕头](https://leetcode.cn/problems/pass-the-pillow/)模拟[6308. 二叉树中的第 K 大层和](https://le…...

【极致简洁】Python tkinter 实现下载工具,你想要的一键获取

嗨害大家好鸭&#xff01;我是小熊猫~开发环境本次项目案例步骤成品效果【咱追求的就是一个简洁】界面如何开始&#xff1f;1.导入模块2.创建窗口【这步很重要】功能按键1.创建一个下拉列表2.设置下拉列表的值3.设置其在界面中出现的位置 column代表列 row 代表行4.设置下拉列表…...

npm i 安装报错

npm WARN EBADENGINE Unsupported engine { npm WARN… npm WARN deprecated stable0.1.8: Modern JS… 诸如此类的报错。大部分都是因为 node 版本问题&#xff01;比如node版本无法满足&#xff0c;对应项目里需要的那些模块和依赖所需要的条件。 有些模块对node版本是有要…...

原腾讯QQ空间负责人,T13专家,黄希彤被爆近期被裁员,裁员原因令人唏嘘。。...

点击上方“码农突围”&#xff0c;马上关注这里是码农充电第一站&#xff0c;回复“666”&#xff0c;获取一份专属大礼包真爱&#xff0c;请设置“星标”或点个“在看这是【码农突围】的第 431 篇原创分享作者 l 突围的鱼来源 l 码农突围&#xff08;ID&#xff1a;smartyuge&…...

【C++】BloomFilter——布隆过滤器

文章目录一、布隆过滤器概念二、布隆过滤器应用三、布隆过滤器实现1.插入2.查找3.删除四、布隆过滤器优缺五、结语一、布隆过滤器概念 布隆过滤器是由布隆&#xff08;Burton Howard Bloom&#xff09;在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构&#xff0c;特点是…...

【Spring】资源操作管理:Resource、ResourceLoader、ResourceLoaderAware;

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 资源操作&#xff1a;Spring Resources一、Res…...

【System Verilog基础】automatic自动存储--用堆栈区存储局部变量

文章目录一、C语言的内存分配&#xff1a;BSS、Data、Text、Heap&#xff08;堆&#xff09;、Stack&#xff08;栈&#xff09;1、1、静态内存分配&#xff1a;BSS、Data1、2、程序执行代码&#xff1a;Text1、3、动态内存分配&#xff1a;Heap&#xff08;堆&#xff09;、St…...

看板组件:Bryntum Task Board JS 5.3.0 Crack

一个超级灵活的看板组件&#xff0c;Bryntum Task Board 是一个灵活的看板 Web 组件&#xff0c;可帮助您可视化和管理您的工作。 功能丰富 任务板非常灵活&#xff0c;允许您完全自定义卡片、列和泳道的渲染和样式。借助丰富的 API&#xff0c;您甚至可以在运行时打开或关闭功…...

45 个 Git 经典操作场景,专治不会合代码

git对于大家应该都不太陌生&#xff0c;熟练使用git已经成为程序员的一项基本技能&#xff0c;尽管在工作中有诸如 Sourcetree这样牛X的客户端工具&#xff0c;使得合并代码变的很方便。但找工作面试和一些需彰显个人实力的场景&#xff0c;仍然需要我们掌握足够多的git命令。下…...

MyBatis之动态SQL

目录 一、<if>标签 二、<trim>标签 三、<where>标签 四、<set>标签 五、<foreach>标签 一、<if>标签 当我们在某个平台提交某些信息时&#xff0c;可能都会遇到这样的问题&#xff0c;有些信息是必填信息&#xff0c;有些信息是非必…...

SpringBoot(Tedu)—DAY01——环境搭建

SpringBoot(Tedu)—DAY01——环境搭建 目录SpringBoot(Tedu)—DAY01——环境搭建零、今日目标一、IDEA2021项目环境搭建1.1 通过 ctrl鼠标滚轮 实现字体大小缩放1.2 自动提示设置 去除大小写匹配1.3 设置参数方法自动提示1.4 设定字符集 要求都使用UTF-8编码1.5 设置自动编译二…...

代理模式-大话设计模式

一、定义 代理模式的定义&#xff1a;为其他对象提供一种代理以控制对这个对象的访问。在某些情况下&#xff0c;一个对象不适合或者不能直接引用另一个对象&#xff0c;而代理对象可以在客户端和目标对象之间起到中介的作用。 著名的代理模式例子为引用计数&#xff08;英语…...

STM32定时器的编码器接口模式

MCU为STM32L431&#xff0c;通用定时器框图&#xff1a; 编码器接口模式一共有三种&#xff0c;通过TIMx_SMCR寄存器的SMS[3:0]位来选择。模式1计数器仅在TI1FP1的边沿根据TI2FP2的电平来判断向上/下计数&#xff1b;模式2计数器仅在TI2FP2的边沿根据TI1FP1的电平来判断向上/下…...

Java方法的使用

目录 一、方法的概念及使用 1、什么是方法(method) 2、方法定义 3、方法调用的执行过程 4、实参和形参的关系 二、方法重载 1、为什么需要方法重载 2、方法重载概念 3、方法签名 三、递归 1、递归的概念 2、递归执行过程分析 一、方法的概念及使用 1、什么是方法(met…...

Linux命令·nl

nl命令在linux系统中用来计算文件中行号。nl 可以将输出的文件内容自动的加上行号&#xff01;其默认的结果与 cat -n 有点不太一样&#xff0c; nl 可以将行号做比较多的显示设计&#xff0c;包括位数与是否自动补齐 0 等等的功能。 1&#xff0e;命令格式&#xff1a;nl [选项…...

排序模型:DIN、DINE、DSIN

目录 DIN 输入 输出&#xff1a; 与transformer注意力机制的区别与联系&#xff1a; DINE 改善DIN 输入&#xff1a; DSIN 动机&#xff1a; DIN 适用与精排&#xff0c;论文&#xff1a; Deep Interest Network for Click-Through Rate Prediction DIN模型提出的动…...

【C++】Clang-Format:代码自动格式化(看这一篇就够了)

文章目录Clang-format格式化C代码1.引言&安装1.1引言1.2 安装2. 配置字解释2.1 language 编程语言2.2 BaseOnStyle 基础风格2.3 AccessModifierOffset 访问性修饰符偏移2.4 AlignAfterOpenBracket 开括号后的对齐2.5 AlignArrayOfStructures 对齐结构体数组2.6 AlignConsec…...

Linux命令·more

more命令&#xff0c;功能类似 cat &#xff0c;cat命令是整个文件的内容从上到下显示在屏幕上。 more会以一页一页的显示方便使用者逐页阅读&#xff0c;而最基本的指令就是按空白键&#xff08;space&#xff09;就往下一页显示&#xff0c;按 b 键就会往回&#xff08;back&…...

为什么 SaaS 公司依靠知识库来做对客户服务?

信不信由你&#xff0c;客户服务是您在软件行业赚钱的核心。不仅仅是拥有出色的产品&#xff0c;不仅仅是拥有出色的营销&#xff0c;更重要的是让人们回到您家门口的客户服务。 这是因为从长远来看&#xff0c;留住现有客户比获得新客户更重要&#xff0c;而留住客户时间更长的…...

后端必备之VUE基础【黑马程序员】

黑马程序员4小时入门VUE传送门 1. 简介 Vue是一个操作JavaScript的框架&#xff0c;类似于jQuery&#xff0c;但比jQuery好用&#xff0c;是现在的主流 2. 测试例子 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /&…...

现代HYUNDAI EDI需求分析

现代集团(HYUNDAI)是韩国一家以建筑、造船、汽车行业为主&#xff0c;兼营钢铁、机械、贸易、运输、水泥生产、冶金、金融、电子工业等几十个行业的综合性企业集团。本文主要介绍HYUNDAI 的EDI需求&#xff0c;带大家快速理清思路&#xff0c;明确EDI项目的推进流程。 通信标准…...

数据库基本功之SQL的基本函数

1. 单行函数与多行函数 1.1 单行函数 指单行数据输入,返回一个值的函数. 所以查询一个表时,对选择的每一行数据都返回一个结果.[oracleoracle-db-19c ~]$ sqlplus / as sysdbaSQL*Plus: Release 19.0.0.0.0 - Production on Tue Mar 7 07:59:44 2023 Version 19.3.0.0.0Copyri…...

配置主机名与ip的映射关系

本次进行简单的小实验 通过在windows上配置主机名与IP地址的映射关系&#xff0c;达到我们在xshell或其他远程连接设备上&#xff0c;不用IP地址登陆&#xff0c;只需要用主机名就能实现登陆的效果 配置 首先 需要查看自己虚拟机的IP地址&#xff0c;找到ens33或者ens160…...

Spring Cache简单介绍和使用

目录 一、简介 二、使用默认ConcurrentMapManager &#xff08;一&#xff09;创建数据库和表 &#xff08;二&#xff09;创建boot项目 &#xff08;三&#xff09;使用Api 1、EnableCaching 2、CachePut 3、cacheable 4、CacheEvict 三、使用redis作为cache 一、简…...

ECCV 2022|面向精确的主动相机定位算法

标题&#xff1a;ECCV 2022,山东大学、北大、腾讯AILab、斯坦福和三维家联合提出&#xff0c;面向精确的主动相机定位算法项目地址&#xff1a;https://github.com/qhFang/AccurateACL.文章&#xff1a;Towards Accurate Active Camera Localization&#xff08;ECCV 2022&…...

web实现环形旋转、圆形、弧形、querySelectorAll、querySelector、clientWidth、sin、cos、PI

文章目录1、HTML部分2、css部分3、JavaScript部分4、微信小程序演示1、HTML部分 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge">&l…...

杭州建德网站建设/万物识别扫一扫

在前几次的Server2008实验里面&#xff0c;我们向服务器FS01、FS02分别添加了一个磁盘分区E盘。但是没有介绍我们是如何通过VMWare Workstation来实现的。今天就让我们来一起利用VMWare Workstation来向虚拟主机添加一块新的硬盘并启用。利用虚拟机做实验&#xff0c;添加硬件&…...

怎样用阿里云服务器做网站/手机网址大全123客户端下载

1. export PKG_CONFIG_PATH$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig/ 2. pkg-config --cflags --libs opencv...

做网站建本地环境作用/seo网站推广的主要目的包括

前面说完了此项目的创建及数据模型设计的过程。如果未看过&#xff0c;可以到这里查看&#xff0c;并且项目源码已经放大到github上,可以去这里下载。代码也已经部署到sina sea上&#xff0c;地址为http://fengzheng.sinaapp.com/先跳过视图展示及表单处理的部分&#xff0c;先…...

网站想建设子站/可以推广赚钱的软件

熟悉C的童鞋都知道&#xff0c;为了避免“野指针”&#xff08;即指针在首次使用之前没有进行初始化&#xff09;的出现&#xff0c;我们声明一个指针后最好马上对其进行初始化操作。如果暂时不明确该指针指向哪个变量&#xff0c;则需要赋予NULL值。除了NULL之外&#xff0c;C…...

国内最大的自建站平台/百度健康

2019独角兽企业重金招聘Python工程师标准>>> 硬件内部计时器精度 US级别 #include<iostream> #include "functional" #include "windows.h" using namespace std;#include <WinBase.h> long long calculateMS(std::function<vo…...

网站建设难做吗/百度推广的渠道有哪些

一.概念 列表视图&#xff1b;用来显示多个可滑动项列表的ViewGroup&#xff1b;需要适配器Adapter 将集合中数据和每一个Item所对应的布局动态适配到ListView中进行显示。 1 <?xml version"1.0" encoding"utf-8"?>2 <LinearLayout xmlns:andro…...