Redis中的事务和原子性
在 Redis 中,事务 和 原子性 是两个关键概念,用于保证多个操作的一致性和可靠性。以下是 Redisson 和 Spring Data Redis 在处理原子性操作时的区别与对比:
1. Redis 的原子性机制
Redis 本身通过以下方式保证原子性:
- 单线程模型:所有命令按顺序执行,单个命令默认是原子的。
- 事务(
MULTI/EXEC
):将多个命令打包为一个事务,按顺序执行,但不支持回滚。 - Lua 脚本:通过
EVAL
执行的 Lua 脚本在 Redis 中是原子执行的,适合复杂逻辑。
2. Redisson 的分布式集合操作(如 putIfAbsent
)
Redisson 封装了 Redis 的分布式数据结构(如 RMap
、RLock
等),其操作默认是线程安全的,并且内部通过 Lua 脚本 或 Redis 事务 保证原子性。
示例:Redisson 的 putIfAbsent
RMap<String, String> map = redisson.getMap("myMap");
String value = map.putIfAbsent("key", "value");
- 实现原理:
Redisson 内部通过 Lua 脚本实现putIfAbsent
,逻辑类似:if redis.call("EXISTS", KEYS[1]) == 0 thenreturn redis.call("SET", KEYS[1], ARGV[1]) elsereturn nil end
- 原子性保障:
由于 Lua 脚本在 Redis 单线程中执行,整个操作是原子的,无需开发者手动处理事务。
优点
- 简化开发:开发者无需关注底层的 Lua 脚本或事务。
- 开箱即用:适合分布式锁、队列、集合等常见场景。
适用场景
- 快速实现分布式集合操作(如
putIfAbsent
、fastPut
)。 - 需要线程安全的分布式数据结构(如
RMap
、RSet
)。
3. Spring Data Redis 的原子性保障
Spring Data Redis 提供了对 Redis 的原生操作支持,但需要开发者通过 Lua 脚本 或 事务 显式保证原子性。
示例:通过 Lua 脚本实现 putIfAbsent
DefaultRedisScript<String> script = new DefaultRedisScript<>();
script.setScriptText("if redis.call('EXISTS', KEYS[1]) == 0 then return redis.call('SET', KEYS[1], ARGV[1]) else return nil end");
script.setResultType(String.class);String result = redisTemplate.execute(script, Collections.singletonList("key"), "value");
示例:通过事务实现原子性
redisTemplate.setEnableTransactionSupport(true);
TransactionStatus status = redisTemplate.getTransactionManager().beginTransaction();
try {redisTemplate.opsForValue().set("key", "value");redisTemplate.boundValueOps("key").get();redisTemplate.getTransactionManager().commit(status);
} catch (Exception e) {redisTemplate.getTransactionManager().rollback(status);
}
原子性保障
- Lua 脚本:通过
EVAL
执行的脚本在 Redis 中是原子的。 - 事务:
MULTI/EXEC
保证命令按顺序执行,但不支持回滚。
优点
- 灵活性:可直接使用 Redis 的原生命令和高级特性。
- 性能优化:通过管道(Pipeline)减少网络往返。
适用场景
- 高频读写缓存(如热点数据)。
- 复杂业务逻辑(如排行榜、分布式计数器)。
- 需要直接调用 Redis 的
SCAN
、BITFIELD
等高级命令。
4. 对比总结
维度 | Redisson | Spring Data Redis |
---|---|---|
原子性保障方式 | 内部封装 Lua 脚本或 Redis 事务,开发者无需手动处理。 | 需要显式使用 Lua 脚本或事务。 |
开发复杂度 | 简单,直接调用 Java 集合接口(如 putIfAbsent )。 | 复杂,需自行编写 Lua 脚本或事务逻辑。 |
性能 | 封装可能引入额外开销(如序列化),适合中低并发场景。 | 原生 Redis 命令调用,性能更高,适合高并发场景。 |
适用场景 | 分布式锁、队列、集合操作等常见场景。 | 缓存、排行榜、复杂数据结构操作等高性能场景。 |
线程安全 | 所有 API 默认线程安全。 | Lettuce 连接线程安全,但需注意多线程操作 Redis 命令的原子性。 |
5. 选择建议
-
优先使用 Redisson:
如果需要快速实现 分布式集合操作(如putIfAbsent
、removeIfPresent
)或 分布式锁(如RLock
),优先选择 Redisson。其封装的 Lua 脚本和事务逻辑隐藏了复杂性,适合快速开发。 -
优先使用 Spring Data Redis + Lua:
如果需要 高性能缓存 或 复杂业务逻辑(如排行榜、分布式计数器),使用 Spring Data Redis 并结合 Lua 脚本。通过精细控制 Redis 命令,可以优化性能并充分利用 Redis 的原生特性。 -
混合使用注意事项:
- 连接池分离:Redisson 和 Spring Data Redis 使用不同的连接池,避免资源竞争。
- 键名命名规范:避免键名冲突(如 Redisson 使用前缀
redisson:
,Spring Data Redis 使用前缀spring:
)。 - 集群模式下的原子性:确保 Lua 脚本操作的 key 在同一个 slot,否则原子性无法保证。
6. 实际案例
案例 1:分布式计数器
-
Redisson:
RAtomicLong counter = redisson.getAtomicLong("counter"); counter.incrementAndGet(); // 原子操作
-
Spring Data Redis:
DefaultRedisScript<Long> script = new DefaultRedisScript<>(); script.setScriptText("return redis.call('INCR', KEYS[1])"); script.setResultType(Long.class); Long count = redisTemplate.execute(script, Collections.singletonList("counter"));
案例 2:分布式锁
-
Redisson:
RLock lock = redisson.getLock("lock"); lock.lock(); try {// 临界区操作 } finally {lock.unlock(); }
-
Spring Data Redis:
String lockKey = "lock"; Boolean isLocked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS); if (Boolean.TRUE.equals(isLocked)) {try {// 临界区操作} finally {redisTemplate.delete(lockKey);} }
通过合理选择工具和实现方式,可以充分发挥 Redis 的原子性保障能力,构建高效、可靠的分布式系统。
相关文章:
Redis中的事务和原子性
在 Redis 中,事务 和 原子性 是两个关键概念,用于保证多个操作的一致性和可靠性。以下是 Redisson 和 Spring Data Redis 在处理原子性操作时的区别与对比: 1. Redis 的原子性机制 Redis 本身通过以下方式保证原子性: 单线程模型…...
怎样把B站的视频保存到本地
在B站(哔哩哔哩)上,有数不清的优质内容,无论是搞笑视频、学习资料,还是动漫影视,总有一些视频让你想反复观看。但是,遇到没有网络或流量不够用的时候,怎么办?把B站的视频…...
Vue3前后端分离用户信息显示方案
在Vue3前后端分离的项目中,若后端仅返回用户ID,可通过以下步骤显示用户名: 解决方案 获取用户信息API 确保后端提供以下任意一种接口: 批量查询接口:传入多个用户ID,返回对应的用户信息列表 单个查询接口…...

DL00987-基于深度学习YOLOv11的红外鸟类目标检测含完整数据集
提升科研能力,精准识别红外鸟类目标! 完整代码数据集见文末 针对科研人员,尤其是研究生们,是否在鸟类目标检测中遇到过数据不够精准、处理困难等问题?现在,我们为你提供一款基于深度学习YOLOv11的红外鸟类…...

黑马程序员C++2024新版笔记 第4章 函数和结构体
1.结构体的基本应用 结构体struct是一种用户自定义的复合数据类型,可以包含不同类型的成员。例如: struct Studet {string name;int age;string gender; } 结构体的声明定义和使用的基本语法: struct 结构体类型 {成员1类型 成员1名称;成…...

数据仓库,扫描量
有五种通用技术用于限制数据的扫描量,正如图3 - 4所示。第一种技术是扫描那些被打上时戳的数据。当一个应用对记录的最近一次变化或更改打上时戳时,数据仓库扫描就能够很有效地进行,因为日期不相符的数据就接触不到了。然而,目前的…...
Day126 | 灵神 | 二叉树 | 层数最深的叶子结点的和
Day126 | 灵神 | 二叉树 | 层数最深的叶子结点的和 1302.层数最深的叶子结点的和 1302. 层数最深叶子节点的和 - 力扣(LeetCode) 思路: 这道题用层序遍历的思路比较好想,就把每层的都算一下,然后返回最后一层的和就…...
Python实例题:人机对战初体验Python基于Pygame实现四子棋游戏
目录 Python实例题 题目 代码实现 实现原理 游戏逻辑: AI 算法: 界面渲染: 关键代码解析 游戏棋盘渲染 AI 决策算法 胜利条件检查 使用说明 安装依赖: 运行游戏: 游戏操作: 扩展建议 增强…...

Vue3性能优化: 大规模列表渲染解决方案
# Vue3性能优化: 大规模列表渲染解决方案 一、背景与挑战 背景 在大规模应用中,Vue3的列表渲染性能一直是开发者关注的焦点。大规模列表渲染往往会导致卡顿、内存占用过高等问题,影响用户体验和系统整体性能。 挑战 渲染大规模列表时,DOM操作…...
笔记:将一个文件服务器上的文件(一个返回文件数据的url)作为另一个http接口的请求参数
笔记:将一个文件服务器上的文件(一个返回文件数据的url)作为另一个http接口的请求参数 最近有这么个需求,需要往某一个业务的外部接口上传文件信息,但是现在没有现成的文件,只在数据库存了对应的url&#…...

【RocketMQ 生产者和消费者】- 生产者启动源码 - MQClientInstance 定时任务(4)
文章目录 1. 前言2. startScheduledTask 启动定时任务2.1 fetchNameServerAddr 拉取名称服务地址2.2 updateTopicRouteInfoFromNameServer 更新 topic 路由信息2.2.1 topic 路由信息2.2.2 updateTopicRouteInfoFromNameServer 获取 topic2.2.3 updateTopicRouteInfoFromNameSer…...

超全GPT-4o 风格提示词案例,持续更新中,附使用方式
本文汇集了各类4o风格提示词的精选案例,从基础指令到复杂任务,从创意写作到专业领域,为您提供全方位的参考和灵感。我们将持续更新这份案例集,确保您始终能够获取最新、最有效的提示词技巧。 让我们一起探索如何通过精心设计的提…...

Android 自定义SnackBar和下滑取消
如何自定义SnackBar 首先我们得了解SnackBar的布局: 之前我看有一些方案是获取内部的contentLayout,然后做一些处理。但是现在已经行不通了: RestrictTo(LIBRARY_GROUP) public static final class SnackbarLayout extends BaseTransientB…...

Netty学习专栏(三):Netty重要组件详解(Future、ByteBuf、Bootstrap)
文章目录 前言一、Future & Promise:异步编程的救星1.1 传统NIO的问题1.2 Netty的解决方案1.3 代码示例:链式异步操作 二、ByteBuf:重新定义数据缓冲区2.1 传统NIO ByteBuffer的缺陷2.2 Netty ByteBuf的解决方案2.3 代码示例:…...

详解 C# 中基于发布-订阅模式的 Messenger 消息传递机制:Messenger.Default.Send/Register
🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C、C#等开发语言,熟悉Java常用开…...

多场景游戏AI新突破!Divide-Fuse-Conquer如何激发大模型“顿悟时刻“?
多场景游戏AI新突破!Divide-Fuse-Conquer如何激发大模型"顿悟时刻"? 大语言模型在强化学习中偶现的"顿悟时刻"引人关注,但多场景游戏中训练不稳定、泛化能力差等问题亟待解决。Divide-Fuse-Conquer方法,通过…...

Java 函数式接口(Functional Interface)
一、理论说明 1. 函数式接口的定义 Java 函数式接口是一种特殊的接口,它只包含一个抽象方法(Single Abstract Method, SAM),但可以包含多个默认方法或静态方法。函数式接口是 Java 8 引入 Lambda 表达式的基础,通过函…...

分布式锁总结
文章目录 分布式锁什么是分布式锁?分布式锁的实现方式基于数据库(mysql)实现基于缓存(redis)多实例并发访问问题演示项目代码(使用redis)配置nginx.confjmeter压测复现问题并发是1,即不产生并发问题并发30测试,产生并发问题(虽然单实例是synchronized&am…...

使用MybatisPlus实现sql日志打印优化
背景: 在排查无忧行后台服务日志时,一个请求可能会包含多个执行的sql,经常会遇到SQL语句与对应参数不连续显示,或者参数较多需要逐个匹配的情况。这种情况下,如果需要还原完整SQL语句就会比较耗时。因此,我…...
springboot中redis的事务的研究
redis的事务类似于队列操作,执行过程分为三步: 开启事务入队操作执行事务 使用到的几个命令如下: 命令说明multi开启一个事务exec事务提交discard事务回滚watch监听key(s):当监听一个key(s)时,如果在本次事务提交之…...
为什么我输入对了密码,还是不能用 su 切换到 root?
“为什么我输入对了密码,还是不能用 su 切换到 root?” 其实这背后可能不是“密码错了”,而是系统不允许你用 su 切 root,即使密码对了。 👇 以下是最常见的几个真正原因: ❌ 1. Root 用户没有设置密码&…...

client.chat.completions.create方法参数详解
response client.chat.completions.create(model"gpt-3.5-turbo", # 必需参数messages[], # 必需参数temperature1.0, # 可选参数max_tokensNone, # 可选参数top_p1.0, # 可选参数frequency_penalty0.0, # 可选参数presenc…...
量子计算与云计算的融合:技术前沿与应用前景
目录 引言 量子计算基础 量子计算的基本原理 量子计算的优势与挑战 量子计算的发展阶段 云计算基础 云计算的基本概念 云计算的应用领域 云计算面临的挑战 量子计算与云计算的结合 量子云计算的概念与架构 量子云计算的服务模式 量子云计算的优势 量子云计算的发展…...
《企业级日志该怎么打?Java日志规范、分层设计与埋点实践》
大家好呀!👋 今天我们要聊一个Java开发中超级重要但又经常被忽视的话题——日志系统!📝 不管你是刚入门的小白,还是工作多年的老司机,日志都是我们每天都要打交道的"好朋友"。那么,如…...
python模块管理环境变量
概要 在 Python 应用中,为了将配置信息与代码分离、增强安全性并支持多环境(开发、测试、生产)运行,使用专门的模块来管理环境变量是最佳实践。常见工具包括: 标准库 os.environ:直接读取操作系统环境变量…...
【泛微系统】后端开发Action常用方法
后端开发Action常用方法 代码实例经验分享:代码实例 经验分享: 本文分享了后端开发中处理工作流Action的常用方法,主要包含以下内容:1) 获取工作流基础信息,如流程ID、节点ID、表单ID等;2) 操作请求信息,包括请求紧急程度、操作类型、用户信息等;3) 表单数据处理,展示…...
【算法】力扣体系分类
第一章 算法基础题型 1.1 排序算法题 1.1.1 冒泡排序相关题 冒泡排序是一种简单的排序算法,它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,…...
sql:如何查询一个数据表字段:Scrp 数据不为空?
在SQL中,要查询一个数据表中的字段 Scrp 不为空的记录,可以使用 IS NOT NULL 条件。以下是一个基本的SQL查询示例: SELECT * FROM your_table_name WHERE Scrp IS NOT NULL;在这个查询中,your_table_name 应该替换为你的实际数据…...

深入浅出人工智能:机器学习、深度学习、强化学习原理详解与对比!
各位朋友,大家好!今天咱们聊聊人工智能领域里最火的“三剑客”:机器学习 (Machine Learning)、深度学习 (Deep Learning) 和 强化学习 (Reinforcement Learning)。 听起来是不是有点高大上? 别怕,我保证把它们讲得明明…...
索引下探(Index Condition Pushdown,简称ICP)
索引下探(Index Condition Pushdown,简称ICP)是一种数据库查询优化技术,常见于MySQL等关系型数据库中。 1. 核心概念 作用:将原本在服务器层执行的WHERE条件判断尽可能下推到存储引擎层执行。减少回表查询次数支持部…...