分布式锁实战:Redisson vs. Redis 原生指令的性能对比
分布式锁实战:Redisson vs. Redis 原生指令的性能对比
引言
在DIY主题模板系统中,用户可自定义聊天室的背景、图标、动画等元素。当多个运营人员或用户同时修改同一模板时,若没有锁机制,可能出现“甲修改了背景色,乙覆盖了甲的修改”的脏写问题。此时,分布式锁成为解决资源互斥访问的核心工具。
市场上常见的分布式锁方案有两种:
- Redis原生指令(如
SET key value NX PX
):轻量、性能高,但需手动处理超时、可重入等边界问题; - Redisson:基于Redis的Java客户端,封装了RedLock算法,提供可重入锁、公平锁等高级特性,但实现复杂度更高。
本文将结合DIY主题模板系统的实际场景,从应用场景、底层原理、常见坑点、压测对比、选型建议五大维度,深入解析两种方案的差异,并给出实战指导。
一、分布式锁的应用场景:DIY主题模板系统的互斥需求
1.1 业务背景
DIY主题模板系统的核心功能是模板配置的增删改查,典型操作流程如下:
- 运营人员通过后台选择模板ID(如
template-123
); - 系统从数据库读取模板当前配置(背景色、图标路径等);
- 运营人员修改配置(如将背景色从#FFFFFF改为#FF0000);
- 系统将新配置写回数据库。
1.2 并发问题与锁需求
当两个运营人员同时修改同一模板时,可能出现以下问题:
- 丢失更新:甲读取旧配置→乙读取旧配置→甲写入新配置→乙写入新配置(覆盖甲的修改);
- 脏数据:甲修改图标路径但未提交→乙基于旧路径修改其他字段→甲回滚导致乙的数据依赖失效。
1.3 分布式锁的价值
通过为模板ID(如template-123
)加锁,确保同一时刻仅一个请求能修改该模板,流程如图1所示:
二、Redisson的底层原理:基于RedLock算法的增强实现
2.1 为什么需要RedLock?
传统的单节点Redis锁(如SET key value NX PX
)存在单点故障风险:若Redis主节点宕机且未同步到从节点,锁可能被重复获取。为解决此问题,Redis作者提出了RedLock算法(Redisson默认实现),通过多个独立Redis实例(通常5个)提升可靠性。
2.2 RedLock的获取与释放流程
RedLock的核心逻辑是:向N个独立Redis节点依次申请锁,若在多数节点(N/2+1)成功获取锁,则认为加锁成功。具体流程如图2所示:
2.3 Redisson的封装与扩展
Redisson基于RedLock算法,提供了以下增强功能:
- 可重入锁:同一线程可多次获取同一锁(通过
lockCount
计数器实现); - 公平锁:按请求顺序分配锁(通过
Semaphore
队列实现); - 锁续期:若业务执行时间超过锁过期时间,自动延长锁的有效期(“看门狗”机制);
- 异步锁:支持
lockAsync()
/unlockAsync()
异步操作,避免阻塞线程。
三、原生Redis指令的坑:从“可用”到“可靠”的距离
3.1 原生Redis锁的基础实现
通过SET key value NX PX
指令可实现基础的分布式锁(NX表示仅当key不存在时设置,PX设置过期时间):
public boolean tryLock(String lockKey, String requestId, int expireTime) {String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime);return "OK".equals(result);
}public void unlock(String lockKey, String requestId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
}
3.2 原生实现的三大致命问题
(1)问题一:超时导致的锁误删
场景:业务执行时间超过锁的过期时间(如锁设置为30秒,业务执行了40秒),锁自动释放后,其他线程获取锁。此时原线程完成业务后,会删除新线程的锁,导致互斥失效。
原因:锁的过期时间与业务执行时间不匹配,且原生实现未提供自动续期机制。
(2)问题二:不可重入导致的死锁
场景:同一线程在未释放锁的情况下,再次尝试获取同一锁(如递归调用),由于锁已存在(NX
条件不满足),加锁失败,导致死锁。
原因:原生Redis锁仅记录“锁是否存在”,未记录“持有锁的线程ID”,无法判断是否是同一线程重复获取。
(3)问题三:单点故障导致的锁失效
场景:Redis主节点宕机,未同步到从节点,新主节点未感知旧锁存在,其他线程可重新获取锁,导致同一资源被多个线程同时修改。
原因:单节点Redis无法保证高可用,锁的可靠性依赖单点。
四、压测对比:Redisson vs. 原生Redis的性能差异
为量化两种方案的性能差异,我们基于DIY主题模板系统的实际场景,设计了以下压测实验:
4.1 压测环境与参数
维度 | 配置/值 |
---|---|
服务器 | 8核16G Linux(CentOS 7) |
Redis集群 | 5节点(3主2从,主节点内存8G) |
压测工具 | JMeter(500线程,循环100次) |
业务场景 | 高并发修改同一模板(锁竞争激烈) |
4.2 压测指标说明
- TPS(每秒事务数):单位时间内成功获取并释放锁的次数;
- 平均延迟(ms):从请求加锁到释放锁的总耗时;
- 锁冲突率(%):加锁失败的请求占比(原生Redis无自动重试,Redisson默认重试3次);
- 锁误删率(%):释放非自己持有的锁的概率。
4.3 压测结果与分析
(1)场景1:短耗时业务(业务执行时间<锁过期时间)
-
原生Redis锁:
- TPS:12000
- 平均延迟:8ms
- 锁冲突率:5%(无重试)
- 锁误删率:0%(业务执行时间<过期时间,无超时)
-
Redisson(RedLock):
- TPS:8000
- 平均延迟:15ms
- 锁冲突率:2%(自动重试3次)
- 锁误删率:0%(看门狗自动续期)
(2)场景2:长耗时业务(业务执行时间>锁过期时间)
-
原生Redis锁:
- TPS:11000
- 平均延迟:9ms
- 锁冲突率:8%(部分请求因锁过期被拒绝)
- 锁误删率:12%(原线程释放已过期的锁)
-
Redisson(RedLock):
- TPS:7500
- 平均延迟:18ms
- 锁冲突率:3%(自动重试+续期)
- 锁误删率:0%(看门狗续期至业务完成)
(3)场景3:Redis主节点宕机(模拟故障)
-
原生Redis锁:
- 锁失效时间:30秒(主从切换耗时)
- 锁冲突率:40%(主节点宕机期间,从节点未同步锁信息)
-
Redisson(RedLock):
- 锁失效时间:5秒(多数节点存活,仍可保证锁有效)
- 锁冲突率:5%(仅需多数节点存活即可维持锁)
4.4 数据结论
维度 | 原生Redis锁 | Redisson(RedLock) |
---|---|---|
性能(TPS) | 高(轻量无额外开销) | 低(需与多个节点交互) |
可靠性 | 低(单点故障/超时误删) | 高(多节点+自动续期) |
开发成本 | 高(需手动处理边界问题) | 低(封装完善,开箱即用) |
五、最佳实践:如何选择分布式锁方案?
5.1 按业务场景选择
(1)选择原生Redis锁的场景:
- 性能敏感:如高频交易系统(每秒上万次锁操作),轻量指令更适合;
- 短耗时业务:业务执行时间明确<锁过期时间(如5秒内),无需续期;
- 弱一致性:允许偶发锁冲突(如用户评论点赞,重复点赞可通过幂等处理)。
(2)选择Redisson的场景:
- 强一致性:如财务系统、配置修改(必须保证互斥);
- 长耗时业务:业务执行时间不确定(如文件上传、复杂计算),需自动续期;
- 高可用要求:系统依赖Redis集群(如电商大促、直播活动),需避免单点故障。
5.2 分布式锁的通用设计原则
- 锁粒度最小化:仅对核心资源加锁(如模板ID),避免锁整个服务;
- 过期时间合理:根据业务执行时间设置(建议为业务耗时的2~3倍),或启用Redisson的看门狗续期;
- 唯一标识防误删:锁值设置为请求唯一ID(如UUID),释放时校验(原生Redis通过Lua脚本实现);
- 监控与报警:记录锁获取失败率、锁持有时间,及时发现异常(如锁未释放导致的死锁)。
六、实战:在DIY主题模板系统中落地
6.1 原生Redis锁的实现(短耗时场景)
// 短耗时业务(如修改模板背景色,耗时约2秒)
public void updateTemplate(String templateId, String newConfig) {String lockKey = "lock:template:" + templateId;String requestId = UUID.randomUUID().toString();int expireTime = 5000; // 过期时间5秒(业务耗时2秒×2.5)// 加锁boolean locked = tryLock(lockKey, requestId, expireTime);if (!locked) {throw new RuntimeException("模板正在被修改,请稍后再试");}try {// 业务逻辑:读取旧配置→修改→写入String oldConfig = templateDao.get(templateId);String mergedConfig = mergeConfig(oldConfig, newConfig);templateDao.update(templateId, mergedConfig);} finally {// 释放锁(通过Lua脚本防误删)unlock(lockKey, requestId);}
}
6.2 Redisson的实现(长耗时场景)
// 长耗时业务(如模板批量上传,耗时约30秒)
public void batchUploadTemplate(String templateId, List<Resource> resources) {RLock lock = redissonClient.getLock("lock:template:" + templateId);try {// 加锁(自动续期,过期时间默认30秒)boolean locked = lock.tryLock(10, TimeUnit.SECONDS); // 最多等待10秒if (!locked) {throw new RuntimeException("模板正在被修改,请稍后再试");}// 业务逻辑:上传资源→生成配置→写入数据库(耗时30秒)for (Resource resource : resources) {ossClient.upload(resource.getPath(), resource.getContent());}String newConfig = generateConfig(resources);templateDao.update(templateId, newConfig);} finally {lock.unlock();}
}
总结
分布式锁的选择没有“最优解”,需结合业务场景(性能要求、一致性等级)、技术成本(开发维护难度)、系统架构(Redis集群规模)综合判断:
- 原生Redis锁适合性能敏感、短耗时、弱一致性的场景,但需手动处理边界问题;
- Redisson适合强一致性、长耗时、高可用的场景,通过封装降低开发成本。
在DIY主题模板系统中,我们对短耗时的“单个配置修改”使用原生Redis锁(TPS高,满足业务需求),对长耗时的“批量资源上传”使用Redisson(避免锁超时误删,保障数据一致性)。
希望本文的实践经验能帮助你在实际项目中做出更合理的选择!
相关文章:

分布式锁实战:Redisson vs. Redis 原生指令的性能对比
分布式锁实战:Redisson vs. Redis 原生指令的性能对比 引言 在DIY主题模板系统中,用户可自定义聊天室的背景、图标、动画等元素。当多个运营人员或用户同时修改同一模板时,若没有锁机制,可能出现“甲修改了背景色,乙…...
MyBatis中foreach集合用法详解
在 MyBatis 中,<foreach> 标签用于遍历集合(Collection、List、Array、Map),常用于构建动态 SQL 语句(如 IN 查询、批量插入等)。以下是详细用法和示例: 核心属性 属性描述collection必填…...

react+taro 开发第五个小程序,解决拼音的学习
1.找一个文件夹 cmd 2.taro init 3.vscode 找开该文件夹cd help-letters 如:我的是(base) PS D:\react\help-letters> pnpm install 4.先编译一下吧。看下开发者工具什么反应。 pnpm dev:weapp 5.开始规则。我用cursor就是不成功。是不是要在这边差不多了&…...
高防IP可以防护什么攻击类型?企业网络安全的第一道防线
“高防IP”成为企业构建网络安全防护体系的重要一环。尤其是对于金融、电商、游戏、政务等业务高度依赖网络稳定性的行业而言,确保系统724小时正常运行已经成为基本要求。高防IP到底可以防护哪些攻击类型?它又是如何帮助企业抵御风险、保障服务稳定运行的…...
Wireshark使用教程(含安装包和安装教程)
Wireshark使用入门教程 0.资源下载以及软件安装1.Wireshark中无法显示网卡列表2.Wireshark抓取H264过程 0.资源下载以及软件安装 参考blog: 抓包神器wireshark安装保姆级教程 压缩包下载:Wireshark安装包 1.Wireshark中无法显示网卡列表 Wireshark中无法显示网…...
Asp.Net Core基于StackExchange Redis 缓存
NuGet安装 StackExchange.Redis Microsoft.Extensions.Options 0. appsettings.json初始化配置 {"Logging": {"LogLevel": {"Default": "Information","Microsoft.AspNetCore": "Warning"}},"AllowedHos…...
【Linux】SSH:免密登录
配置 SSH 的免密登录(基于公钥认证)可实现无需输入密码即可登录远程主机,常用于自动化脚本、服务器集群、DevOps 等场景。 生成本地 SSH 密钥对(若尚未存在) 在本地客户端执行: ssh-keygen -t rsa -b 409…...

kafka(windows)
目录 介绍 下载 配置 测试 介绍 Kafka是一个分布式流媒体平台,类似于消息队列或企业信息传递系统。 下载 Kafka对于Zookeeper是强依赖,所以安装Kafka之前必须先安装zookeeper 官网:Apache Kafka 下载此安装包并解压 配置 新建log…...
深度学习习题3
1.训练神经网络过程中,损失函数在一些时期(Epoch)不再减小, 原因可能是: 1.学习率太低 2.正则参数太大 3.卡在了局部最小值 A1 and 2 B. 2 and 3 C. 1 and 3 D. 都是 2.对于分类任务,我们不是将神经网络中的随机权重…...
勒让德多项式
勒让德多项式 (Legendre) 当区间为 [ − 1 , 1 ] [-1,1] [−1,1],权函数 ρ ( x ) 1 ρ(x)1 ρ(x)1时,由 1 , x , . . . , x n , . . . {1,x,...,x^n,...} 1,x,...,xn,...正交化得到的多项式称为勒让德多项式,并用 P 0 ( x ) , P 1 ( x ) ,…...
atc abc409E
原题链接:E - Pair Annihilation 题目背景: n 个点 n - 1 条边的有权无向图,每个点都有一个值,两个连通的点的值可以互相抵消,既将u 的 -1 传给 v 时可以抵消掉 v 的 1 并花费边权值;求最小花费。 考察算…...
Mysql批处理写入数据库
在学习mybatisPlus时,看到一个原本没用过的参数: rewriteBatchedStatementstrue 将上述代码装入jdbc的url中即可使数据库启用批处理写入。 需要注意的是,这个参数仅适用于MySQL JDBC 驱动的私有扩展参数。 作用原理是: 原本的…...

基于安卓的文件管理器程序开发研究源码数据库文档
摘 要 伴随着现代科技的发展潮流,移动互联网技术快速发展,各种基于通信技术的移动终端设备做的也越来越好了,现代智能手机大量的进入到了我们的生活中。电子产品的各种软硬技术技术的发展,操作系统的不断更新换代,谷歌…...

EMC VNXe 存储系统日志收集方法
写在前面 有朋友找来看看VNXe的故障,这种问题总是要收集日志,顺便这里也分享给大家。 注意,VNXe和VNX 属于完全不同的产品,不要看名字很类似,操作系统已经完全重构了,如果说是否有联系,大概就…...
嵌入式链表操作原理详解
嵌入式链表操作原理详解 链表是嵌入式软件开发中最基础的数据结构之一,其设计采用嵌入式链表节点的思想,实现了高度通用的链表管理机制。以下是核心原理和操作的全面解析: 一、基础数据结构 struct list_head {struct list_head *next, *pr…...

从“人找政策”到“政策找人”:智能退税ERP数字化重构外贸生态
离境退税新政核心内容与外贸企业影响 (一)政策核心变化解析 退税商店网络扩容 新政明确鼓励在大型商圈、旅游景区、交通枢纽等境外旅客聚集地增设退税商店,并放宽备案条件至纳税信用M级企业。以上海为例,静安区计划新增1000家退…...
一.设计模式的基本概念
一.核心概念 对软件设计中重复出现问题的成熟解决方案,提供代码可重用性、可维护性和扩展性保障。核心原则包括: 1.1. 单一职责原则 定义:一个类只承担一个职责,避免因职责过多导致的代码耦合。 1.2. 开闭原则 定义…...

以人类演示视频为提示,学习可泛化的机器人策略
25年5月来自清华大学、上海姚期智研究院和星动纪元(RoboEra)公司的论文“Learning Generalizable Robot Policy with Human Demonstration Video as a Prompt”。 最近的机器人学习方法通常依赖于从通过遥操作收集的大量机器人数据集中进行模仿学习…...
split方法
在编程中,split 方法通常用于将字符串按照指定的分隔符拆分成多个部分,并返回一个包含拆分结果的列表(或数组)。不同编程语言中的 split 方法语法略有不同,但核心功能相似。以下是常见语言中的用法: 1. P…...

SOC-ESP32S3部分:36-适配自己的板卡
飞书文档https://x509p6c8to.feishu.cn/wiki/RP4UwPrsKi4xuQkKLAAcKxD3n1b 如果你自己画了PCB板,需要把自己绘制的板卡配置小智AI工程,可以参考此文档。 下载源码 克隆或下载源码到本地,这里以1.5.5为例,大家可以自行修改其它版…...

LLMs 系列科普文(8)
八、模型的自我认知 接下来我们聊聊另一种问题,即模型的自我认知。 网上经常经常可以看到人们会问大语言模型一些关于认知方面的问题,比如“你是什么模型?谁创造了你?” 说实话,其实这个问题有点无厘头。 之所以这么…...
【明日方舟 × 红黑树】干员调度如何不掉线?算法工程的平衡魔法全揭秘!
【明日方舟 红黑树】干员调度如何不掉线?算法工程的平衡魔法全揭秘! 作者:星之辰 标签:#红黑树 #明日方舟 #工程平衡树 #算法科普 #动态数据结构 引子:为什么你的干员调度能实时平衡,从不崩盘?…...
Vue3 + Vite 中使用 Lodash-es 的防抖 debounce 详解
Vue3 Vite 中使用 Lodash-es 的防抖(debounce)详解 在 Vue3 Vite 项目中,debounce 是 lodash-es 中最常用的功能之一,它可以帮助我们优化高频事件的处理。下面我将详细讲解 debounce 的使用方法,并提供一个完整的示例。 Debounce 核心概念…...

机器学习基础相关问题
机器学习相关的基础问题 K-means是否一定会收敛 K-means是否一定会收敛 K-means算法在有限步数内一定会收敛,但收敛到的可能是局部最优解而非全局最优解。以下是详细分析: K-means 的优化目标是最小化 样本到其所归属簇中心的距离平方和(SSE…...

验证负载均衡与弹性伸缩
什么是弹性伸缩(Auto Scaling)? 弹性伸缩是指 云计算平台根据实时负载自动调整计算资源(如服务器实例、容器Pod)数量,以确保系统在高峰时保持稳定,在低谷时节省成本。 什么时候会触发弹性伸缩&…...

Three.js中AR实现详解并详细介绍基于图像标记模式AR生成的详细步骤
文档地址 Three.js中AR实现详解 以下是Three.js中实现AR功能的详细解析,涵盖技术原理、实现步骤、核心组件及优化策略: 🧩 一、技术基础 AR.js框架的核心作用 AR.js是Three.js实现AR的基石,提供以下核心能力: 多模…...
CSS高级技巧及新增属性
CSS高级技巧及新增属性 jarringslee 文章目录 CSS高级技巧及新增属性精灵图 Sprite字体图标 iconfontCSS几何图形的写法更改鼠标样式更改表单轮廓取消文本域的拖拽行内块元素的垂直居中对齐溢出文字处理 CSS布局技巧CSS5新增内容及其他属性新增选择器新增基础属性及其他属性ca…...

GeoBoundaries下载行政区划边界数据(提供中国资源shapefile)
要下载山东省济南市各个区的行政区划边界数据,你可以通过 geoBoundaries 提供的数据来实现。下面是详细步骤,包括网页操作和可选的 Python 自动化方式。 目录 ✅ 一、通过 geoBoundaries 官网手动下载1. 打开官网:2. 查找中国数据:…...
《深入理解 Nacos 集群与 Raft 协议》系列四:日志复制机制:Raft 如何确保提交可靠且幂等
《深入理解 Nacos 集群与 Raft 协议》系列 大家好,我是G探险者! 在前几篇中我们介绍了选主与日志对比机制,它们保证了“谁能成为 Leader”以及“Leader 的日志是否可靠”。 而当 Leader 已选定,系统需要把客户端的写请求写入所…...

大模型如何选型?嵌入模型如何选型?
欢迎来到啾啾的博客🐱。 记录学习点滴。分享工作思考和实用技巧,偶尔也分享一些杂谈💬。 有很多很多不足的地方,欢迎评论交流,感谢您的阅读和评论😄。 目录 引言模型优劣认知与模型选择大模型(L…...