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

SpringBoot中使用redis事务

本文基于SpringBoot 2.X

事务在关系型数据库的开发中经常用到,其实非关系型数据库,比如redis也有对事务的支持,本文主要探讨在SpringBoot中如何使用redis事务。

事务的相关介绍可以参考:

0、起因

在一次线上事故中,我们定位到redis的使用存在大value,超过了dubbo的最大数据量限制,于是紧急将这个大的对象value拆分成单个的string value。

为了保持数据库和redis双写一致,在对数据库进行更新,删除,插入操作时,要从redis删除指定的key。

一切都是使用redis的常规操作,但雷就埋在其中一个数据库的update方法里,这个方法上开启了事务@Transactional,导致里面的删除redis key操作也加入了事务。

上线后出现报错:

这个报错明确指出,集群模式的redis不支持事务。集群不支持事务的原因可参考此文:Is there any Redis client (Java prefered) which supports transactions on Redis cluster?

1、Spring中的事务

所有数据访问技术都有事务机制,这些技术提供了API来开启事务、提交事务完成数据操作, 或者在发生错误的时候回滚数据。

Spring采用统一的机制来处理不同的数据访问技术的事务, Spring的事务提供一个PlatformTransactionManager的接口,不同的数据访问技术使用不同的接口实现。

数据访问技术实现
JDBCDataSourceTransactionManager
JPAJPATransactionManager
HibernateHibernateTransactionManager
JDOJDOTransactionManager
分布式事务JtaTransactionManager
  • 在SpringBoot中开启事务非常简单,只需要在方法或类上使用注解@Transactional即可。
  • Spring官方文档中还要求使用@EnableTransactionManagement 开启事务,但SpringBoot通过自动配置已经帮我们做了,所以SpringBoot中不用写该注解

@Transactional注解的几个常用属性

  • propagation

事务的传播机制,主要有以下几种,默认是REQUIRED

  1. REQUIRED - 方法A调用时候没有事务新建一个事务,在方法A中调用方法B,将使用相同的事务,如果方法B发生异常需要回滚,整个事务回滚。
     

  2. REQUIRES_NEW - 方法A调用方法B时,无论是否存在事务都开启一个新事务,这样B方法异常不会导致A的数据回滚。
     

  3. NESTED - 和REQUIRES_NEW类似,但是只支持JDBC,不支持JPA或Hibernate

  4. SUPPORTS - 方法调用时有事务就用事务,没事务就不用事务

  5. NOT_SUPPORTED - 强制方法不在事务中执行,若有事务,在方法调用到结束阶段先挂起事务。

  6. NEVER - 强制不能有事务,若有事务就抛出异常

  7. MANDATORY - 强制必须有事务,如果没有事务就抛出异常

  • rollbackFor

指定哪些异常可以导致事务回滚,默认是Throwable的子类

  • noRollbackFor

执行哪些异常不可用引起事务回滚,默认是Throwable的子类

2、@Transactional事务失效的情况

  1. 只对public方法生效。默认的protected和private方法上写上@Transactional不会报错,但该方法上的事务不生效,官方原文:Method visibility and @Transactional;
  2. 默认情况(只写@Transactional不填写rollbackFor参数)下此注解会对unchecked异常进行回滚,对checked异常不回滚;
  3. 类内部未开启事务的方法调用开启事务的方法

针对3,引用丁雪丰的《Spring全家桶》视频中的解释:

Spring的声明式事务本质上是通过AOP来增强了类的功能

Spring的AOP本质上就是为类做了一个代理

看似在调用自己写的类,实际用的是增强后的代理类

下图描述了方法被事务代理时的流程,来源:Spring AOP
 

3、SpringBoot整合Redis事务实践

下面我们搭建一个最简单的SpringBoot整合redis的工程用代码来验证redis事务

  • SpringBoot整合Redis

SpringBoot整合redis使用的是spring-boot-starter-data-redis,redis事务依赖于jdbc的事务管理,所以还需要引入jdbc

pom相关引入:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope>
</dependency>
  • 开启Redis事务

编写redis配置类,开启redis事务,配置事务管理

@Configuration
public class RedisConfig {@Beanpublic StringRedisTemplate StringRedisTemplate(RedisConnectionFactory factory) {StringRedisTemplate template = new StringRedisTemplate(factory);/*** description 开启redis事务(仅支持单机,不支持cluster)**/template.setEnableTransactionSupport(true);return template;}/*** description 配置事务管理器**/@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource){return new DataSourceTransactionManager(dataSource);}
}
  • 代码验证

针对本文讨论,设计了四个验证方法,可自行验证

/*** description 不带事务set* return java.lang.String* author 郑晓龙* createTime 2019/12/12 16:36**/
@GetMapping("put")
public void put(String key, String value) {redisService.put(key, value);
}
/*** description 带事务set* return java.lang.String* author 郑晓龙* createTime 2019/12/12 16:36**/
@GetMapping("putWithTx")
public void putWithTx(String key, String value) {redisService.putWithTx(key, value);
}
/*** description 调用带事务方法不生效的情况* return java.lang.String* author 郑晓龙* createTime 2019/12/12 16:36**/
@GetMapping("invokeWithPutTx")
public void invokeWithPutTx(String key, String value) {redisService.invokePutWithTx(key, value);
}
/*** description 调用带事务方法生效的情况* return java.lang.String* author 郑晓龙* createTime 2019/12/12 16:36**/
@GetMapping("invokeWithPutTx2")
public void invokeWithPutTx2(String key, String value) {redisService.invokePutWithTx2(key, value);
}

4、总结:

  • redis事务只支持单机,不支持cluster
  • 需要开启事务时,只需要在对应的方法或类上使用@Transactional注解即可,SpringBoot自动开启了@EnableTransactionManagement
  • 需要注意事务不生效的几种情况
  • redis事务依赖于jdbc的事务管理

5、示例代码及参考:

示例代码: redis-transaction

  1. Transaction Management
  2. Transaction Propagation
  3. Transactional Support
  4. 《Spring全家桶》丁雪丰

相关文章:

SpringBoot中使用redis事务

本文基于SpringBoot 2.X 事务在关系型数据库的开发中经常用到&#xff0c;其实非关系型数据库&#xff0c;比如redis也有对事务的支持&#xff0c;本文主要探讨在SpringBoot中如何使用redis事务。 事务的相关介绍可以参考&#xff1a; 0、起因 在一次线上事故中&#xff0c;我们…...

2023全网汇总PMP备考攻略(附答题技巧)

一&#xff0c;多复习和学习新版考纲 01《PMBOK》看三遍 这边建议看三遍《PMBOK》&#xff0c;更有利于我们巩固知识&#xff0c;查缺补漏。 第一遍 第一遍是老师带着我们去看。这个时候一定要非常专心&#xff0c;千万不要上课走神或者玩手机。因为这一遍老师会告诉我们&a…...

lightdb/pg reload guc 参数机制

lightdb/pg reload guc 参数机制 本文主要讲述调用pg_reload_conf 后&#xff0c;到guc被真正修改之间发送的故事。(基于pg13) pg_reload_conf 函数实现如下&#xff1a; Datum pg_reload_conf(PG_FUNCTION_ARGS) {if (kill(PostmasterPid, SIGHUP)){ereport(WARNING,(errms…...

E. Archaeology(纯思维)

Problem - E - Codeforces 爱丽丝买了一个刚果总理视频的订阅&#xff0c;正在看一部关于苏格兰卡特林湖的因子岛的考古发现的纪录片。考古学家发现了一本书&#xff0c;其年代和来源都不明。也许爱丽丝可以对它进行一些解释&#xff1f; 这本书包含一串字符 "a"、&…...

FISCO BCOS(三十四)———商品溯源(智能合约+后端)

FISCO BCOS(三十四)———商品溯源(智能合约+后端) 一、智能合约函数调用流程 注:智能合约来源(官网的合约仓库中) 但是TraceabilityFactory合约有问题,我已经做了修改,可以看原版与我的,只有一个函数不同。 官网上这套合约在TraceabilityFactory这个合约上缺少getGo…...

ts体操训练

1 实现pick type MyPick<T, K extends keyof T> {[P in K]: T[P] }2 实现readonly 让interface中所有属性变为可读 type MyReadonly<T> {readonly [K in keyof T]: T[K] }3 TupleToObject 将元组类型转换为对象类型 type tupleToObject<T extends any[]&…...

int指令

格式&#xff1a; int n,n为中断类型码&#xff0c;它的功能是引发中断过程 CPU执行过程 取中断类型码n&#xff1b;标志寄存器入栈&#xff0c;IF0&#xff0c;TF0&#xff1b;CS&#xff0c;IP入栈&#xff1b;&#xff08;IP&#xff09;&#xff08;n4&#xff09;&…...

Cycling 74 Max for Mac:音乐可视化编程软件

Cycling 74 Max是一款音乐、视觉、互动艺术等领域中广泛使用的编程语言和应用软件&#xff0c;它允许用户创作和控制实时音频和视频效果、交互式应用程序和媒体艺术品等。 Max将程序设计和可视化编程相结合&#xff0c;通过简单的拖拽和连接方式&#xff0c;用户可以将各种功能…...

ROS学习第十二节——话题通信控制小乌龟

1.基操一下 首先打开小乌龟程序和键盘控制程序 rosrun turtlesim turtlesim_node rosrun turtlesim turtle_teleop_key 查看话题列表 rostopic list 打开计算图查看具体是那个话题在起作用 rqt_graph 从上图可以看到两个节点之间的话题是 /turtle1/cmd_vel 使用以下命令获…...

matlab点云的可视化-源码复制粘贴即可(一)

一、导入并可视化一个无属性的点云 clc; clear; close; % clear everything% Import a point cloud from a plain text file (run type(Lion.xyz) to see the contents of the file) pc pointCloud(Lion.xyz);% Generate a z-colored view of the point cloud pc.plot;% Set …...

反射-Class类分析

反射相关的主要类 java.lang.Class&#xff1a;代表一个类&#xff0c;Class对象表示某个类加载后在堆中的对象java.lang.reflect.Method&#xff1a;代表类的方法&#xff0c;Method对象表示某个类的方法java.lang.reflect.Field&#xff1a;代表类的成员变量&#xff0c;Fie…...

Let’s Make C++ Great Again——string与常用字符处理函数

文章目录 string使用string类的例子&#xff0c;统计一个字符串中单词的个数&#xff1a;在算法模拟题中翻转字符串&#xff1a;判断回文字符串&#xff1a;字符串查找&#xff1a;字符串替换&#xff1a; 常用字符处理函数strlen()strcpy()strcat()strcmp()toupper() 和 tolow…...

〖Python网络爬虫实战⑰〗- 网页解析利器parsel实战

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 说明&#xff1a;本专栏持续更新中&#xff0c;目前专栏免费订阅&#xff0c;在转为付费专栏前订阅本专栏的&#xff0c;可以免费订阅付…...

中电金信:生成式AI热潮下,文本智能走向何方?

突破通用人工智能场景&#xff0c;生成式AI正在向全行业应用进攻。 一个脑筋急转弯&#xff0c;几个月前ChatGPT是这样回答的&#xff1a; 然而&#xff0c;仅仅几个月的迭代&#xff0c;它的回答却让人出乎意料。 看似调侃的对比背后实则是无数次模型训练的支撑。基于数据的激…...

探索Linux设备树:硬件描述与驱动程序的桥梁

目录标题 引言&#xff1a;Linux设备树简介 | Introduction: Linux Device Tree Overviewa. 设备树的背景与发展 | Background and Development of Device Treeb. 设备树的作用与意义 | The Role and Significance of Device Tree 设备树语法与结构 | Device Tree Syntax and S…...

UNION ALL用法 以及 UNION ALL和UNION的区别

部分参考自文章&#xff1a; https://blog.csdn.net/a200822146085/article/details/119545374(CC 4.0 BY-SA版权协议)CSDN「我心依依旧」 https://www.1keydata.com/cn/sql/sql-unionall.php SQL Union All SQL指令 UNION ALL用法 UNION ALL 这个指令的目的也是要将两个 SQL 语…...

Ubuntu Linux操作

引言 晚上上课发现桌子上遗留了这本书&#xff0c;水课就看了看学习下&#xff0c;以下内容直接总结知识点 磁盘内存解析 (1)硬盘有数个盘片,每个盘片两个面,每个面一个磁头。 (2)盘片被划分为多个扇形区域即扇区。 (3)同一盘片不同半径的同心圆为磁道。 (4)不同盘片相同半径…...

MongoDB常用语句(CURD)

文章目录 一、数据库操作二、集合操作三、文档操作3.1 插入文档3.2 查询文档3.3 更新文档3.4 删除文档 四、安全认证4.1 创建管理员账号4.2 创建应用数据库用户4.3 启动和连接 (校验方式) 提示&#xff1a;以下是本篇文章正文内容&#xff0c;MongoDB 系列学习将会持续更新 一…...

一篇文章让你彻底学会--节流(并且自己可以手写)

Hi,有的小伙伴们在面试的时候会被要求手写节流函数&#xff0c;很多都被难着了吧&#xff0c;宝贝&#xff0c;那你你没有理解节流函数。 今天&#xff0c;就让我带你攻克它&#xff01; 1.节流 单位时间内&#xff0c;事件触发&#xff0c;最多只执行一次事件回调。 人话:说…...

C++ 形参是类的指针 class * 通过new的方式创建对象

当你在C中使用类指针&#xff08;class *&#xff09;作为函数的形参&#xff0c;并通过 new 关键字创建对象时&#xff0c;这种用法确实会改变类对象的值。原因是你通过指针传递了对象的内存地址&#xff0c;而不是传递对象本身。这意味着在函数内部对对象的任何修改都会直接影…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

恶补电源:1.电桥

一、元器件的选择 搜索并选择电桥&#xff0c;再multisim中选择FWB&#xff0c;就有各种型号的电桥: 电桥是用来干嘛的呢&#xff1f; 它是一个由四个二极管搭成的“桥梁”形状的电路&#xff0c;用来把交流电&#xff08;AC&#xff09;变成直流电&#xff08;DC&#xff09;。…...

职坐标物联网全栈开发全流程解析

物联网全栈开发涵盖从物理设备到上层应用的完整技术链路&#xff0c;其核心流程可归纳为四大模块&#xff1a;感知层数据采集、网络层协议交互、平台层资源管理及应用层功能实现。每个模块的技术选型与实现方式直接影响系统性能与扩展性&#xff0c;例如传感器选型需平衡精度与…...