Redis的事务和锁以及在SpringBoot中的使用

news2025/1/11 18:37:56

文章目录

        • 1、事务
        • 2、监视锁
        • 3、分布式锁

1、事务

Redis中事务的操作主要有三个:

# 1、开启事务
# 定事务的开启位置,此指令执行后,后续的所有指令均加入到事务中
1、multi
# 2、执行事务
# 设定事务的结束位置,同时执行事务。与multi成对出现,成对使用
# 加入事务的命令暂时进入到任务队列中,并没有立即执行,只有执行exec命令才开始执行
2、exec
# 3、取消事务
# 终止当前事务的定义,发生在multi之后,exec之前
3、discard

已经执行完毕的命令对应的数据不会自动回滚,需要程序员自己在代码中实现回滚。

接下来介绍下Redis事务在SpringBoot中的使用,这里就忽略掉Redis配置文档以及Dependency引用相关部分的内容了,如果需要可以参考博文:【Redis】 springboot集成Redis

这里先给出一个错误的使用示范

	public void useTransaction() {
		redisTemplate.setEnableTransactionSupport(true);
			
		// 开启事务
		redisTemplate.multi();
		try {
			redisTemplate.opsForValue().setIfAbsent("test0210", "0210");
			int a=1/0;
			redisTemplate.opsForValue().setIfAbsent("test0210_01", "0210_01");
			redisTemplate.exec();
		} catch (Exception e) {
			redisTemplate.discard();
			System.out.println(e.getMessage());
		}
	}

使用上述的代码来进行Redis事务的执行时,会报io.lettuce.core.RedisCommandExecutionException: ERR DISCARD without MULTI的错误,具体的原因可以参考博文:SpringBoot项目中使用Redis事务出现ERR EXEC without MULTI的分析与总结

正确的使用方式如下所示:

	public void useTransaction() {	
		
		SessionCallback<Object> session=new SessionCallback<Object>() {			
			@Override
			public  List<Object> execute(RedisOperations operations) throws DataAccessException {
				operations.multi();
				operations.opsForValue().setIfAbsent("test0210", "0210");
				int a=1/0;
				operations.opsForValue().setIfAbsent("test0210_01", "0210_01");
				return operations.exec();
			}
		};
		
		try {
			List<Object> results= (List<Object>) redisTemplate.execute(session);
			System.out.println("Number of items added to set: " + results.get(0));
		} catch (Exception e) {		
			System.out.println(e.getMessage()); // e.getMessage为/by zero
		}
	}

以上为正确的使用方式,如上所示,当有Exception触发时,会触发事务的回滚机制;官网说明地址:使用SessionCallback接口操作redis事务

SessionCallback与RedisCallback两者的关系:
SessionCallback可以看作是RedisCallback的封装,但是SessionCallback更好用,可读性更高;RedisCallback更偏向于底层,操作相对复杂一些。

除了以上的方式,还有另外一种方式也可以实现Redis事务,来源于Spring官方文档-RedisTemplate对@Transactional的支持
在这里插入图片描述

2、监视锁

监视锁的操作命令如下:

# 对key添加监视锁,在执行exec前如果key发生了变化,中止事务执行
1、watch key1 [key2....]
# 取消对所有key的监视
2、unwatch

使用RedisTemplate客户端来实现监视锁的代码如下:

	public void useWatchLock() {
		String name="name",sex="sex";
		List<String> keys=new ArrayList<>();
		keys.add(name);
		keys.add(sex);
		
		SessionCallback<List<Object>> session=new SessionCallback<List<Object>>() {
			@Override
			public List<Object> execute(RedisOperations operations) throws DataAccessException {
				operations.watch(keys);
				operations.multi();
				operations.opsForValue().set(name, "lisi");
				operations.opsForValue().set(sex, "male");
				return operations.exec();
			}
		};	
		List<Object> results=redisTemplate.execute(session);		
		results.stream().forEach(x->System.out.println(x));
	}

执行上述代码两次,第一次正常执行;第二次在exec之前打断点,然后通过客户端修改key为name对应的value值,两次结果如下:

// 第一次结果:true true
// 第二次结果:     
// 第二次结果为空说明,事务已经被中止执行,添加监视锁成功。

3、分布式锁

分布式锁的实现,如果要实现分布式锁,就要使用到如下命令:

# 使用setNX命令来设置一个公共锁
1、setnx lock-key value

具体的过程如下:
1、利用setNX命令的特征,有值则返回设置失败,无值则返回设置成功;
     如果setNX命令返回设置成功,用户控制权,进行下一步具体的业务操作;
     对于setNX命令返回设置失败,不具备控制权,排队或等待;
2、操作完毕通过del操作释放锁。

但是使用以上的实现方式存在一定的风险,由于锁操作由用户控制加锁解锁,必定会存在加锁后未解锁的风险,可以使用一下方式来进行优化:

# 使用expire为锁key添加时间限定,到时不释放,则放弃锁
1、expire lock-key second
2、pexpire lock-key milliseconds

接下来介绍下分布式锁在SpringBoot中的使用:

	public void useSetNX() {
		String lock = "lock_nx";
		String restoreKey="number";
		Boolean isExisted=redisTemplate.hasKey(restoreKey);
		if (!isExisted) {
			 redisTemplate.opsForValue().setIfAbsent(restoreKey, "10");
		}
		Integer number =Integer.valueOf((String) redisTemplate.opsForValue().get(restoreKey));
		if (number <= 0) {
			System.out.println("本场商品已清仓,请关注店铺公告,下次再来!!!");
			return;
		}
		// 尝试加锁,設置超時時間
		Boolean lockStatus = redisTemplate.opsForValue().setIfAbsent(lock, String.valueOf(Math.round(Math.random())), 2,
				TimeUnit.SECONDS);
		if (lockStatus) {
			System.out.println("成功获得锁,开始购买商品!!!");
			long result = redisTemplate.opsForValue().decrement("number");
			System.out.println(result);
			// 尝试解锁
			lockStatus = redisTemplate.delete(lock);
			System.out.println("解锁" + (lockStatus == true ? "成功" : "失败") + "!!!");
		} else {
			System.out.println("其他用户正在执行,请稍后.....");
		}
	}

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

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

相关文章

【java】map集合遍历: entrySet()、单独遍历key或者value、 keySet()、iterator遍历

目录 1.增强for循环。利用Map 的 entrySet&#xff08;&#xff09;方法获取元素2.增强for循环。单独遍历key或者value3.增强for循环。利用Map 的 keySet() 方法获取元素4.使用iterator遍历文中实体的定义在上篇笔记中&#xff1a;https://blog.csdn.net/qq_43622777/article/d…

spring面试题 一

一、为了降低Java开发的复杂性&#xff0c;Spring采取了那4种关键策略 基于POJO的轻量级和最小侵入性编程&#xff1b; 通过依赖注入和面向接口实现松耦合&#xff1b; 基于切面和惯例进行声明式编程&#xff1b; 通过切面和模板减少样板式代码。 二、Spring框架的核心&am…

14条生产环境项目踩坑复盘

缓存需要等事务结束之后再删除&#xff0c;避免旧数据导致数据库和缓存不一致。说明&#xff1a;比如线程A在减少账户的余额(11->10)&#xff0c;执行了数据库更新&#xff0c;但是事务还未提交&#xff0c;但把缓存删除了。线程B从缓存里去获取账户的余额&#xff0c;缓存里…

Python Numpy基础教程

本文是一个关于Python numpy的基础学习教程&#xff0c;其中&#xff0c;Python版本为Python 3.x 什么是Numpy Numpy Numerical Python&#xff0c;它是Python中科学计算的核心库&#xff0c;可以高效的处理多维数组的计算。并且&#xff0c;因为它的许多底层函数是用C语言编…

TC358775XBG替代方案|完美替代 TC358775XBG替代方案|低BOM成本DSI转LVDS方案CS5518

TC358775XBG替代方案|完美替代 TC358775XBG替代方案|低BOM成本DSI转LVDS方案CS5518 TC358775XBG芯片的主要功能是DSI到LVDS桥&#xff0c;通过DSI链路实现视频流输出&#xff0c;以驱动LVDS兼容的显示面板。该芯片支持单链路LVDS高达1366768 24位像素分辨率&#xff0c;双链路L…

了解webpack

文章目录一、webpack是什么&#xff1f;二、为什么要使用webpack三、webpack的五个核心概念四、安装webpack提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、webpack是什么&#xff1f; 本质上&#xff0c;webpack 是一个用于现代 JavaScript 应用程…

PyTorch学习笔记:nn.MSELoss——MSE损失

PyTorch学习笔记&#xff1a;nn.MSELoss——MSE损失 torch.nn.MSELoss(size_average None&#xff0c;reduce None&#xff0c;reduction mean)功能&#xff1a;创建一个平方误差(MSE)损失函数&#xff0c;又称为L2损失&#xff1a; l(x,y)L{l1,…,lN}T,ln(xn−yn)2l(x,y)L…

设计模式:建造者模式教你创建复杂对象

一、问题场景 当我们需要创建资源池配置对象的时候&#xff0c;资源池配置类里面有以下成员变量: 如果我们使用new关键字调用构造函数&#xff0c;构造函数参数列表就会太长。 如果我们使用set方法设置字段值&#xff0c;那minIdle<maxIdle<maxTotal的约束逻辑就没地方…

【计组】内存和总线

课程链接&#xff1a;深入浅出计算机组成原理_组成原理_计算机基础-极客时间 一、虚拟内存和内存保护 日常使用的操作系统下&#xff0c;程序不能直接访问物理内存。内存需要被分成固定大小的页&#xff08;Page&#xff09;&#xff0c;再通过虚拟内存地址&#xff08;Virtu…

如何创建一个有效的FAQ/常见问题解答页面?

如果您的网站上没有常见问题解答页面&#xff0c;那么您将错过本可以节省的数小时的销售和支持时间&#xff0c;以及本可以推动购买的新客户。尽管与登录页面或销售页面不同&#xff0c;常见问题解答页面显然不会显示直接的投资回报&#xff0c;但它可以在其他因素上节省您的团…

内网渗透(十五)之内网信息收集-域环境中定位域管理员

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

多路查找树

1.二叉树与 B 树 1.1二叉树的问题分析 二叉树的操作效率较高&#xff0c;但是也存在问题, 请看下面的二叉树 二叉树需要加载到内存的&#xff0c;如果二叉树的节点少&#xff0c;没有什么问题&#xff0c;但是如果二叉树的节点很多(比如 1 亿)&#xff0c; 就 存在如下问题:问…

洞察:2022年医疗行业数据安全回顾及2023年展望

过去的2022年&#xff0c;统筹安全与发展&#xff0c;在医疗信息化发展道路中&#xff0c;数据安全不可或缺。这一年&#xff0c;实施五年多的《网络安全法》迎来首次修改&#xff0c;《数据安全法》、《个人信息保护法》实施一周年&#xff0c;配套的《数据出境安全评估办法》…

用户标签体系建设

一、什么是标签体系 通过给每个用户打标&#xff0c;方便后续通过用户标签来快速筛选用户&#xff0c;给不同的用户满足不一样的需求。 标签体系也能给不同的用户标签群体&#xff0c;推送不同的内容&#xff0c;从而达到精准营销的目的。 二、标签体系实施架构 标签体系架…

盾王电力防护、森林防火预警应急广播方案

适用场景&#xff1a;针对普通路人与进入有涉电危险的区域人员作区别预警&#xff1b;三重音频、广告牌子宣传、LED显示、视频监控、预警干预。主要功能 第一重预警&#xff1a;对岸边上的行人&#xff0c;当你进入有涉电危险的警戒12米范围内。人体感应器探测到会马上发出语音…

光纤、光模块、光纤交换机、光模块组网设计与案例

光纤组网已是当今智能化弱电行业里一种常见的组网方式&#xff0c;组建远距离无线、监控网络时&#xff0c;往往需要使用光纤进行连接通信&#xff0c;使用光纤收发器是经济适用型做法&#xff0c;尤其是在室外的使用。其实光纤收发器不仅可以成对使用&#xff0c;还可以配合光…

ESP-IDF:在客户端网页上实现拍照按钮功能,并跳转新页面显示图片

ESP-IDF:在客户网页上实现拍照按钮功能&#xff0c;并跳转新页面显示图片 核心代码&#xff1a; /* Send response with custom headers and body set as the* string passed in user context*/ //const char* resp_str (const char*) req->user_ctx; const char *resp_s…

MASA Stack 1.0 发布会讲稿——实践篇

MASA Stack 1.0 实践篇 产品智能化 产品智能化的改造怎么做&#xff1f; 我们以采用运营商网络场景的物联网架构举例&#xff0c;如图从左到右&#xff0c;在设备端我们研发了一款净水行业通用的物联网盒子&#xff0c;它带有各种传感器&#xff0c;如TDS、温度、流量、漏水检…

80%的代码AI帮你写?还没这么夸张,不过也快了

兔年春节一过&#xff0c;APIcat进入到云服务版本的开发阶段&#xff0c;过年前发生了一件大事&#xff0c;Chatgpt横空出世&#xff0c;不少人预测Chatgpt会替代的10大行业&#xff0c;其中就有程序员。 这时&#xff0c;一位特斯拉的老哥出来说&#xff0c;GitHub Copilot帮…

Redis简介

Redis是一款开源的、高性能的键-值存储&#xff08;key-value store&#xff09;。它常被称作是一款数据结构服务器&#xff08;data structure server&#xff09;。 Redis的键值可以包括字符串&#xff08;strings&#xff09;类型&#xff0c;同时它还包括哈希&#xff08;h…