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

Java 高并发场景下 Redis 分布式锁(UUID+Lua)最佳实践

一、核心原理Redis 分布式锁的设计基石1.1 分布式锁的核心要求一款可靠的分布式锁需满足以下 4 点核心要求否则易引发死锁、锁误删、数据不一致等问题互斥性同一时间只有一个线程能持有锁杜绝并发竞争安全性仅持有锁的线程能释放锁防止误删其他线程的锁防死锁锁需设置过期时间避免线程持有锁后宕机导致锁永久占用高可用加锁、解锁操作高效适配高并发场景不成为性能瓶颈。1.2 UUIDLua 方案的核心逻辑本方案通过“Redis 原子加锁 UUID 唯一标识 Lua 原子解锁”三者结合满足上述要求UUID 唯一标识作为锁的 Value 值绑定加锁线程确保“锁归属唯一”。UUID 全局唯一可避免分布式场景下多服务、多线程锁归属误判替代线程 ID进程内唯一跨进程易重复Redis 原子加锁使用setIfAbsentSETNX操作原子性完成“锁不存在则设置 过期时间”避免加锁与设置过期时间分离导致的死锁Lua 原子解锁通过 Lua 脚本原子执行“判断锁归属 删除锁”避免“判断”与“删除”两步操作分离导致的锁误删。二、完整实现通用 Redis 分布式锁工具类import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Component; import org.springframework.util.Assert; import java.util.Collections; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * 通用 Redis 分布式等待锁工具类UUIDLua 方案 * 核心能力超时等待加锁、原子解锁适配高并发场景 */ Component public class GenericRedisWaitLock { private final StringRedisTemplate stringRedisTemplate; // Lua 原子解锁脚本验证锁归属UUID后删除避免误删 private static final String UNLOCK_LUA_SCRIPT if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end ; // 重试间隔50ms平衡重试效率与 CPU 占用 private static final long DEFAULT_RETRY_INTERVAL 50; // 构造器注入 Redis 模板Spring 自动装配 public GenericRedisWaitLock(StringRedisTemplate stringRedisTemplate) { this.stringRedisTemplate stringRedisTemplate; } /** * 尝试获取分布式锁支持自定义等待时间单位过期时间固定为秒 * param lockKey 锁键如lock:device:1001 * param waitTime 最大等待时间超时后放弃 * param waitTimeUnit 等待时间单位秒/毫秒等 * param expireTimeSec 锁过期时间秒必须0防止死锁 * return 锁标识UUID成功返回标识失败返回 null */ public String tryLock(String lockKey, long waitTime, TimeUnit waitTimeUnit, long expireTimeSec) { // 入参校验避免非法参数导致异常 Assert.hasText(lockKey, lockKey 不能为空); Assert.isTrue(waitTime 0, waitTime 不能为负数); Assert.isTrue(expireTimeSec 0, expireTimeSec 必须大于 0); Assert.notNull(waitTimeUnit, waitTimeUnit 不能为空); // 生成 UUID 作为锁标识绑定当前线程 String lockValue UUID.randomUUID().toString(); // 转换等待时间为毫秒计算截止时间 long waitTimeMs waitTimeUnit.toMillis(waitTime); long deadline System.currentTimeMillis() waitTimeMs; // 循环尝试加锁直到超时 while (System.currentTimeMillis() deadline) { // 原子加锁不存在则设置值 过期时间 Boolean lockSuccess stringRedisTemplate.opsForValue() .setIfAbsent(lockKey, lockValue, expireTimeSec, TimeUnit.SECONDS); // 加锁成功返回 UUID 标识解锁时需传入 if (Boolean.TRUE.equals(lockSuccess)) { return lockValue; } // 加锁失败短暂休眠后重试避免 CPU 空转 try { Thread.sleep(DEFAULT_RETRY_INTERVAL); } catch (InterruptedException e) { // 捕获中断异常恢复线程状态并退出 Thread.currentThread().interrupt(); return null; } } // 等待超时返回 null 表示加锁失败 return null; } /** * 重载方法等待时间与过期时间均为秒极简调用 * param lockKey 锁键 * param waitTimeSec 最大等待时间秒 * param expireTimeSec 锁过期时间秒 * return 锁标识UUID/null */ public String tryLock(String lockKey, long waitTimeSec, long expireTimeSec) { return tryLock(lockKey, waitTimeSec, TimeUnit.SECONDS, expireTimeSec); } /** * 原子释放分布式锁 * param lockKey 锁键与加锁时一致 * param lockValue 锁标识加锁时返回的 UUID * return true解锁成功false锁不存在/非当前线程持有 */ public boolean unlock(String lockKey, String lockValue) { // 空值快速失败避免空指针与无效解锁 if (lockKey null || lockValue null) { return false; } // 初始化 Lua 脚本 DefaultRedisScriptLong unlockScript new DefaultRedisScript(); unlockScript.setScriptText(UNLOCK_LUA_SCRIPT); unlockScript.setResultType(Long.class); // 执行 Lua 脚本原子判断并删除锁 Long executeResult stringRedisTemplate.execute( unlockScript, Collections.singletonList(lockKey), // KEYS[1] 锁键 lockValue // ARGV[1] UUID 标识 ); // 结果为 1 表示解锁成功0 表示锁归属不匹配/已过期 return executeResult ! null executeResult 1; } }三、实战使用高并发场景示例以“设备序号递增”和“FDT 巡检记录创建”两个典型高并发场景为例演示工具类的使用方式重点体现“加锁-执行业务-解锁”的完整流程。3.1 场景一设备序号递增避免重复/跳号import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; Service public class DeviceSeqService { private final GenericRedisWaitLock redisWaitLock; private final StringRedisTemplate stringRedisTemplate; // 设备序号缓存键 private static final String DEVICE_SEQ_KEY device:seq:current; // 设备序号锁键 private static final String DEVICE_SEQ_LOCK_KEY lock:device:seq; // 加锁配置最多等 3 秒锁过期 10 秒 private static final long WAIT_TIME_SEC 3; private static final long EXPIRE_TIME_SEC 10; public DeviceSeqService(GenericRedisWaitLock redisWaitLock, StringRedisTemplate stringRedisTemplate) { this.redisWaitLock redisWaitLock; this.stringRedisTemplate stringRedisTemplate; } // 生成下一个设备序号高并发安全 public Long generateNextSeq() { String lockValue null; try { // 1. 获取分布式锁 lockValue redisWaitLock.tryLock(DEVICE_SEQ_LOCK_KEY, WAIT_TIME_SEC, EXPIRE_TIME_SEC); if (lockValue null) { throw new RuntimeException(获取锁超时序号生成失败); } // 2. 一查二判三更新临界区业务 String currentSeqStr stringRedisTemplate.opsForValue().get(DEVICE_SEQ_KEY); Long currentSeq currentSeqStr null ? 0 : Long.parseLong(currentSeqStr); if (currentSeq 0) { throw new RuntimeException(设备序号异常); } Long newSeq currentSeq 1; stringRedisTemplate.opsForValue().set(DEVICE_SEQ_KEY, newSeq.toString()); return newSeq; } finally { // 3. 最终释放锁必须在 finally 中确保锁释放 if (lockValue ! null) { redisWaitLock.unlock(DEVICE_SEQ_LOCK_KEY, lockValue); } } } }3.2 场景二FDT 巡检记录创建避免重复创建import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; Service public class FdtInspectService { private final GenericRedisWaitLock redisWaitLock; private final StringRedisTemplate stringRedisTemplate; // 巡检任务缓存键前缀设备 ID 为后缀 private static final String INSPECT_TASK_PREFIX fdt:inspect:task:; // 巡检锁键前缀设备 ID 为后缀细粒度锁 private static final String INSPECT_LOCK_PREFIX lock:fdt:inspect:; // 加锁配置 private static final long WAIT_TIME_SEC 3; private static final long EXPIRE_TIME_SEC 10; public FdtInspectService(GenericRedisWaitLock redisWaitLock, StringRedisTemplate stringRedisTemplate) { this.redisWaitLock redisWaitLock; this.stringRedisTemplate stringRedisTemplate; } // 创建设备巡检记录高并发安全 public boolean createInspectTask(String deviceId) { String lockKey INSPECT_LOCK_PREFIX deviceId; String taskKey INSPECT_TASK_PREFIX deviceId; String lockValue null; try { // 1. 获取设备维度的细粒度锁不影响其他设备并发 lockValue redisWaitLock.tryLock(lockKey, WAIT_TIME_SEC, EXPIRE_TIME_SEC); if (lockValue null) { return false; // 加锁失败说明已有采集器处理该设备 } // 2. 一查二判三更新临界区业务 String taskStatus stringRedisTemplate.opsForValue().get(taskKey); if (RUNNING.equals(taskStatus)) { return false; // 已存在巡检任务不重复创建 } // 创建设备任务设置为运行中状态有效期 30 分钟 stringRedisTemplate.opsForValue().set(taskKey, RUNNING, 30, java.util.concurrent.TimeUnit.MINUTES); // 模拟巡检记录入库逻辑 saveInspectTaskToDb(deviceId); return true; } finally { // 3. 释放锁 if (lockValue ! null) { redisWaitLock.unlock(lockKey, lockValue); } } } // 模拟巡检记录入库 private void saveInspectTaskToDb(String deviceId) { // 实际业务中替换为数据库写入逻辑 } }四、关键注意事项与最佳实践4.1 锁的粒度设计优先使用细粒度锁如场景二中按设备 ID 加锁而非全局锁。细粒度锁可减少锁竞争提升系统并发能力全局锁会导致所有线程排队成为性能瓶颈。4.2 过期时间设置锁过期时间expireTimeSec需满足过期时间 业务最大耗时建议设置为业务最大耗时的 1.5-2 倍。例如业务最多执行 5 秒过期时间可设为 10 秒避免锁提前过期导致并发问题。4.3 解锁操作规范解锁必须传入加锁时返回的 UUID 标识否则 Lua 脚本会拒绝解锁防止误删解锁操作必须放在finally块中确保无论业务执行成功还是异常锁都能最终释放解锁失败仅需日志记录无需抛异常锁已自动过期不影响数据一致性。4.4 加锁失败处理加锁失败返回 null时可根据业务场景选择处理方式抛异常适用于必须获取锁才能执行的业务如序号生成返回失败适用于非核心业务如巡检记录创建有限重试通过循环重试获取锁需控制重试次数避免无限循环。4.5 避免常见坑不直接使用delete解锁直接删除锁会导致误删其他线程的锁必须用 Lua 脚本原子解锁不忽略中断异常捕获InterruptedException后需恢复线程中断状态避免线程状态错乱不使用线程 ID 作为锁标识线程 ID 跨进程易重复无法满足分布式场景需求。五、总结Redis 分布式锁UUIDLua方案通过 UUID 保证锁归属唯一通过 Lua 脚本保证解锁原子性结合 Redis 高性能原子操作可完美解决分布式高并发场景下的数据一致性问题。本文提供的工具类轻量化、无业务耦合可直接复用实战示例覆盖了常见高并发场景给出了细粒度锁、过期时间配置等最佳实践。在实际开发中需根据业务特点调整加锁等待时间、过期时间合理设计锁粒度避开常见坑点才能充分发挥该方案的优势构建高可用、高并发的分布式系统。

相关文章:

Java 高并发场景下 Redis 分布式锁(UUID+Lua)最佳实践

一、核心原理:Redis 分布式锁的设计基石1.1 分布式锁的核心要求一款可靠的分布式锁需满足以下 4 点核心要求,否则易引发死锁、锁误删、数据不一致等问题:互斥性:同一时间只有一个线程能持有锁,杜绝并发竞争&#xff1b…...

“我写的提示词生成了代码”——这算原创吗?(中国首例AI提示词著作权案庭审纪要精要)

第一章:智能代码生成与知识产权问题 2026奇点智能技术大会(https://ml-summit.org) 智能代码生成工具(如GitHub Copilot、Tabnine、CodeWhisperer)已深度融入现代开发流程,但其训练数据多源自公开代码仓库(包括GitHub…...

GLM-OCR驱动微信小程序开发:拍照取字与实时翻译

GLM-OCR驱动微信小程序开发:拍照取字与实时翻译 你有没有遇到过这样的场景?看到一份外文菜单、一份产品说明书,或者一份手写的笔记,想要快速提取上面的文字并翻译成中文,却只能一个字一个字地敲,或者来回切…...

基于cv_resnet101_face-detection_cvpr22papermogface的Java集成实战:SpringBoot服务调用

基于cv_resnet101_face-detection_cvpr22papermogface的Java集成实战:SpringBoot服务调用 想象一下,你正在为一个金融App开发用户实名认证功能,或者为一个社区门禁系统设计人脸通行模块。核心需求很明确:用户上传一张照片&#x…...

忍者像素绘卷一文详解:Z-Image-Turbo-rinaiqiao checkpoint深度解析

忍者像素绘卷一文详解:Z-Image-Turbo-rinaiqiao checkpoint深度解析 1. 产品概述与核心价值 忍者像素绘卷是一款基于Z-Image-Turbo深度优化的图像生成工作站,专为二次元风格和复古像素艺术创作而设计。它通过独特的视觉设计和强大的技术架构&#xff0…...

Qwen3-4B模型入门教程:部署后如何确认服务正常并开始使用?

Qwen3-4B模型入门教程:部署后如何确认服务正常并开始使用? 1. 教程目标与准备工作 刚部署完Qwen3-4B模型,你可能会有这样的疑问:服务真的跑起来了吗?怎么知道模型已经准备好接受请求了?本教程将带你一步步…...

YOLO12视频流扩展:OpenCV逐帧捕获+YOLO12 API调用代码实例

YOLO12视频流扩展:OpenCV逐帧捕获YOLO12 API调用代码实例 1. 引言 实时视频分析是计算机视觉领域最激动人心的应用之一。想象一下,你需要监控一个停车场,实时统计车辆进出;或者开发一个智能家居系统,自动识别家庭成员…...

一物一码有哪些公司:快消企业如何选择合适服务商

一物一码有哪些公司:快消企业如何选择合适服务商在快消行业,渠道费用越来越高、终端动销越来越难、消费者触达越来越分散,已经成为许多企业的共同感受。相比单点式促销工具,一物一码正在从“营销活动手段”演变为“渠道、用户与产…...

AbMole 丨 FIN56 通过降解 GPX4 与调控 CoQ10 诱导铁死亡

FIN56(AbMole,M6731)是一种铁死亡(ferroptosis)诱导剂[1],其作用机理具有双重性:一方面,FIN56通过诱导谷胱甘肽过氧化物酶4(GPX4)蛋白的降解来触发铁死亡&…...

福瑞康系统APP开发功能实例分析

客户端:APP模式,H5为邀请注册码地址 服务端开发语言:PHP,JAVA 后端UI框架:‌Element Plus‌,它是基于Vue 3‌框架开发的UI组件库‌,旨在帮助开发者快速构建现代化的用户界面 。由 饿了么前端团队…...

重生之从0开始学习c++之模板初级

1. 泛型编程 —— 为什么需要模板? 如何实现一个通用的交换函数呢? void Swap(int& left, int& right) { int temp left; left right; right temp; } void Swap(double& left, double& right) { double temp left; left right; rig…...

c++怎么编写多线程安全的跨平台文件日志库_无锁队列与异步IO【附源码】

因为 std::ofstream 不是线程安全的,多个线程同时调用其 write() 等成员函数会引发数据竞争,导致未定义行为、崩溃或日志错乱。为什么直接用 std::ofstream 多线程写日志会崩多个线程同时调用 std::ofstream::write() 或 std::ios_base::failure 异常或进…...

ESP32 BLE蓝牙AT指令实战:跨厂商模块透传配置与避坑指南

1. ESP32 BLE蓝牙透传入门指南 第一次接触ESP32 BLE蓝牙透传的朋友可能会觉得有点懵,其实说白了就是让两个蓝牙设备像对讲机一样自由收发数据。我最近刚用ESP32和亿佰特E104-BT5011A模块完成了这个项目,过程中踩了不少坑,今天就把完整流程和避…...

SAP ECC6 EC-CS 合并报表操作手册(完整版)

SAP ECC6 EC-CS 合并报表操作手册(完整版)适用版本:ECC6.0(含 EHP)模块:EC-CS(Enterprise Controlling – Consolidation)核心用途:法定合并、管理合并、内部交易抵销、股…...

进阶提升!MySQL存储过程、触发器与视图实操指南

前三篇我们依次掌握了MySQL基础CRUD、进阶查询、事务、索引及数据备份,已经能满足日常开发和企业级基础数据操作需求。但在实际工作中,经常会遇到重复执行的SQL操作(如批量处理数据)、需要自动触发的业务逻辑(如数据插…...

# Bug 报告:openai-codex provider broken since 2026.4.5 �� Cloudflare challenge + missing OAuth scope /

Bug 报告:openai-codex provider broken since 2026.4.5 �� Cloudflare challenge + missing OAuth scope / openai-codex provider broken since 2026.4.5 - Cloudflare challenge + missing OAuth scope 链接: https://blog.csdn.net/cosmoslife 作者: cosmoslife 日期: 2…...

共探智能医疗与人工智能的新时代 | IHAI 2026

探索智能医疗与AI的未来 | IHAI 2026 国际会议 地点: 中国玉溪会议简介2026年智能医疗与人工智能国际会议(IHAI 2026)将于本年度在中国玉溪举行。这座融合了自然美景与多元文化的古城将迎来全球智能医疗和人工智能领域的顶尖专家、学者及行业领袖。本次大…...

告别乱码!手把手教你用LvglFontTool为LVGL嵌入式UI制作中文字库(附SPI Flash/SD卡存储方案)

嵌入式UI开发实战:LVGL中文字库高效制作与存储方案全解析 在嵌入式系统开发中,图形用户界面(GUI)的中文显示一直是开发者面临的棘手问题。当你在STM32或ESP32上使用LVGL构建交互界面时,是否遇到过这样的场景:精心设计的界面在显示…...

【硬件】2026最适合做家用NAS的CPU是哪一款

家用NAS没有绝对“唯一最优”的CPU,核心是匹配你的预算、功能需求和功耗预期。结合2026年的市场现状、软件兼容性和实测表现,以下是分场景的精准推荐,覆盖99%的家用需求,同时附上核心选型原则和避坑指南。 核心选型黄金原则&#…...

s2-pro参数调优指南:Max New Tokens与Chunk Length对语音连贯性影响

s2-pro参数调优指南:Max New Tokens与Chunk Length对语音连贯性影响 1. 引言 s2-pro作为Fish Audio开源的专业级语音合成模型镜像,在文本转语音领域表现出色。它不仅支持基础的文本转语音功能,还能通过参考音频复用特定音色,为语…...

告别printf调试:用NRF52832的UART串口实现高效日志输出(附SDK15.3配置)

NRF52832串口日志实战:从printf到高效调试的进阶之路 调试嵌入式系统就像在黑暗房间里找钥匙——传统printf调试如同每次开灯看一眼又关上,而UART日志系统则是装上了智能照明,让问题无处遁形。对于NRF52832这样的低功耗蓝牙芯片开发者来说&am…...

商城小程序,不只是卖货这么简单

在数字化浪潮席卷各行各业的今天,商城小程序早已不是新鲜事物。但真正把商城小程序做深、做透,让它适配千行百业的差异化需求,却并非一件容易的事。我们深耕软件开发多年,发现很多客户对商城小程序的认知还停留在“线上摆个摊”的…...

洛谷 P1381 单词背诵

题目描述灵梦有 n 个单词想要背,但她想通过一篇文章中的一段来记住这些单词。文章由 m 个单词构成,她想在文章中找出连续的一段,其中包含最多的她想要背的单词(重复的只算一个)。并且在背诵的单词量尽量多的情况下&…...

批量归一化基础:让模型训练更稳定

文章目录前言一、没BN的深度学习有多难?先懂痛点再学技术1.1 内部协变量偏移:网络每层都在“乱变”1.2 梯度消失与爆炸:深层网络的“拦路虎”1.3 调参难如登天:对初始化和学习率极度敏感二、批量归一化到底是什么?一句…...

常见网络攻击

DDoS攻击,CC攻击 CC攻击 DDoS的一种 发送大量的合法请求消耗应用层的资源(CPU,内存,数据等),耗尽资源,比如在教务网站中写死循环脚本持续访问某个资源,无技术难度 防御: 对同IP限流,验证码,行为分析 DDoS 全名分布式拒绝攻击,攻击者控制大量僵尸设备(被植入病毒的电脑,服…...

NaViL-9B多模态模型应用:智能识别图片内容,轻松实现图文对话

NaViL-9B多模态模型应用:智能识别图片内容,轻松实现图文对话 1. NaViL-9B模型概述 NaViL-9B是上海人工智能实验室研发的原生多模态大语言模型,具备同时处理文本和图像信息的能力。与传统的单一模态模型不同,NaViL-9B能够理解图片…...

从linspace到logspace:Matlab新手必须掌握的两种‘间距’生成函数对比指南

从linspace到logspace:Matlab新手必须掌握的两种‘间距’生成函数对比指南 刚接触Matlab的工程师或科研人员,常常会被各种数据生成函数弄得眼花缭乱。特别是在需要创建特定间隔的数值序列时,linspace和logspace这两个看似简单的函数&#xff…...

企业级大模型API中转站实测对比:主线、备线怎么排更合理

很多团队做大模型接入时,会先问一个看起来很直接的问题:哪家 API 中转站更强。可只要项目进入正式阶段,你就会发现,这个问题本身问得还不够工程化。更现实的问法通常是:谁适合做主线,谁适合做备线&#xff…...

品牌却从未出现在 AI 搜索回答推荐中

说真的,现在绝大多数品牌在 AI 搜索里拿不到曝光,根本不是内容发的少,也不是全网营销做的不好,是从根上就没搞懂,AI 搜索选内容、推品牌的逻辑,和传统的全网营销,完全是两码事。截至 2025 年 12…...

Flux Sea Studio 性能基准测试:不同GPU型号下的生成速度对比

Flux Sea Studio 性能基准测试:不同GPU型号下的生成速度对比 最近在折腾AI生图,特别是用Flux Sea Studio,发现一个挺实际的问题:选什么GPU?是咬牙上顶配的RTX 4090,还是性价比更高的RTX 3080?它…...