基于Redis分布锁+事务补偿解决数据不一致性问题
基于Redis的分布式设备库存服务设计与实现
概述
本文介绍一个基于Redis实现的分布式设备库存服务方案,通过分布式锁、重试机制和事务补偿等关键技术,保证在并发场景下库存操作的原子性和一致性。该方案适用于物联网设备管理、分布式资源调度等场景。
代码实现
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;// 模拟设备库存服务
public class DeviceInventoryService {private static final Logger logger = LoggerFactory.getLogger(DeviceInventoryService.class);private final Map<String, Integer> inventoryMap = new HashMap<>();private static final int MAX_RETRIES = 3;private static final int LOCK_EXPIRE_TIME = 10; // 锁的过期时间,单位:秒private final Jedis jedis;public DeviceInventoryService(Jedis jedis) {this.jedis = jedis;}// 初始化库存public void initializeInventory(String deviceId, int quantity) {inventoryMap.put(deviceId, quantity);logger.info("设备 {} 初始化库存为 {}", deviceId, quantity);}// 尝试获取分布式锁private boolean tryLock(String lockKey) {SetParams setParams = SetParams.setParams().nx().ex(LOCK_EXPIRE_TIME);String result = jedis.set(lockKey, "locked", setParams);return "OK".equals(result);}// 释放分布式锁private void releaseLock(String lockKey) {jedis.del(lockKey);}// 定时更新库存public boolean updateInventory(String deviceId, int updateQuantity) {String lockKey = "inventory_lock:" + deviceId;int retries = 0;//重试次数while (retries < MAX_RETRIES) {if (tryLock(lockKey)) {try {return doUpdateInventory(deviceId, updateQuantity);} catch (Exception e) {logger.error("设备 {} 库存更新失败,重试第 {} 次", deviceId, retries + 1, e);} finally {releaseLock(lockKey);}}retries++;try {Thread.sleep(100); // 等待一段时间后重试} catch (InterruptedException e) {Thread.currentThread().interrupt();}}logger.error("设备 {} 库存更新失败,达到最大重试次数", deviceId);return false;}// 实际执行库存更新操作private boolean doUpdateInventory(String deviceId, int updateQuantity) {int oldQuantity = inventoryMap.getOrDefault(deviceId, 0);try {// 记录操作日志logger.info("设备 {} 开始更新库存,更新前库存: {}", deviceId, oldQuantity);// 模拟更新操作int newQuantity = oldQuantity + updateQuantity;if (newQuantity < 0) {throw new IllegalArgumentException("库存不能为负数");}inventoryMap.put(deviceId, newQuantity);logger.info("设备 {} 库存更新成功,当前库存: {}", deviceId, newQuantity);return true;} catch (Exception e) {logger.error("设备 {} 库存更新失败: {}", deviceId, e.getMessage());// 进行事务补偿compensateInventory(deviceId, oldQuantity);return false;}}// 事务补偿private void compensateInventory(String deviceId, int oldQuantity) {inventoryMap.put(deviceId, oldQuantity);logger.info("设备 {} 库存已恢复到更新前的状态,当前库存: {}", deviceId, oldQuantity);}// 模拟定时任务public static void main(String[] args) {try (Jedis jedis = new Jedis("localhost", 6379)) {DeviceInventoryService service = new DeviceInventoryService(jedis);service.initializeInventory("device001", 10);// 模拟定时更新库存service.updateInventory("device001", 5);service.updateInventory("device001", -20); // 模拟更新失败}}}
核心设计
分布式锁机制
private boolean tryLock(String lockKey) {SetParams setParams = SetParams.setParams().nx().ex(LOCK_EXPIRE_TIME);String result = jedis.set(lockKey, "locked", setParams);return "OK".equals(result);
}
- 使用Redis的set nx ex命令实现原子性加锁
- 将锁的颗粒度设置到了设备上(根据实际业务设置)
- 设置10秒过期时间,防止死锁(根据实际业务设置过期时间)
重试机制
int retries = 0;//重试次数while (retries < MAX_RETRIES) {if (tryLock(lockKey)) {try {return doUpdateInventory(deviceId, updateQuantity);} catch (Exception e) {logger.error("设备 {} 库存更新失败,重试第 {} 次", deviceId, retries + 1, e);} finally {releaseLock(lockKey);}}retries++;try {Thread.sleep(100); // 等待一段时间后重试} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
- 最大重试次数三次(MAX_RETRIES)
- 如果没有获取到锁则等待重试,超过重试次数则终止
补偿机制
private void compensateInventory(String deviceId, int oldQuantity) {inventoryMap.put(deviceId, oldQuantity);logger.info("设备 {} 库存已恢复到更新前的状态,当前库存: {}", deviceId, oldQuantity);
}
- 在doUpdateInventory捕获异常后自动回滚
- 基于版本号/快照的恢复机制
- 保证最终数据一致性
关键代码解析
public boolean updateInventory(String deviceId, int updateQuantity) {String lockKey = "inventory_lock:" + deviceId;int retries = 0;while (retries < MAX_RETRIES) {if (tryLock(lockKey)) {try {return doUpdateInventory(deviceId, updateQuantity);} finally {releaseLock(lockKey);}}// ...重试逻辑...}return false;
}
- 获取设备级别的分布式锁
- 执行库存更新操作
- 无论成功失败都释放锁(finally保证)
- 达到重试上限后返回失败
核心操作方法
private boolean doUpdateInventory(String deviceId, int updateQuantity) {int oldQuantity = inventoryMap.getOrDefault(deviceId, 0);int newQuantity = oldQuantity + updateQuantity;if (newQuantity < 0) {throw new IllegalArgumentException("库存不能为负数");}inventoryMap.put(deviceId, newQuantity);return true;
}
- 前置校验:库存不能为负数
- 原子性操作:库存增减计算
- 事务性更新:先计算后写入
使用示例
初始化与测试
public static void main(String[] args) {try (Jedis jedis = new Jedis("localhost", 6379)) {DeviceInventoryService service = new DeviceInventoryService(jedis);service.initializeInventory("device001", 10);service.updateInventory("device001", 5); // 成功:库存15service.updateInventory("device001", -20); // 失败:触发补偿}
}
预期输出
INFO - 设备 device001 初始化库存为 10
INFO - 设备 device001 开始更新库存,更新前库存: 10
INFO - 设备 device001 库存更新成功,当前库存: 15
INFO - 设备 device001 开始更新库存,更新前库存: 15
ERROR - 设备 device001 库存更新失败: 库存不能为负数
INFO - 设备 device001 库存已恢复到更新前的状态,当前库存: 15
扩展思考
优化方向
- Redis集群支持:当前为单节点Redis,可升级为Redis Cluster
- 锁续期机制:添加看门狗线程自动续期锁
- 库存持久化:结合数据库实现库存持久化存储
- 监控体系:添加Prometheus监控指标
注意事项
- 网络分区场景下可能出现锁状态不一致
- 库存更新操作应保持幂等性
- Redis连接需要配置合理的超时参数
- 生产环境建议使用Lua脚本保证原子性
通过本文实现的库存服务,在保证线程安全的基础上,能够有效应对分布式环境下的资源竞争问题。实际部署时建议结合具体业务场景进行压力测试和参数调优。
相关文章:
基于Redis分布锁+事务补偿解决数据不一致性问题
基于Redis的分布式设备库存服务设计与实现 概述 本文介绍一个基于Redis实现的分布式设备库存服务方案,通过分布式锁、重试机制和事务补偿等关键技术,保证在并发场景下库存操作的原子性和一致性。该方案适用于物联网设备管理、分布式资源调度等场景。 …...
虚拟电商-延迟任务系统的微服务改造(二)注册中心和Feign调用
一、微服务注册中心Consul 编写完延迟任务系统的web层接口,也就是说可以基于http协议来访问延迟系统,接下来要将延迟任务改造成一个服务。首要考虑的问题就是服务的注册与发现,服务的注册与发现都离不开服务的注册中心,本项目选取…...
数智读书笔记系列022《算力网络-云网融合2.0时代的网络架构与关键技术》读书笔记
一、书籍核心价值与定位 1.1 书籍概述:中国联通研究院的权威之作 《算力网络 —— 云网融合 2.0 时代的网络架构与关键技术》由中国联通研究院算力网络攻关团队精心撰写,是业界首部系统性探讨云网融合 2.0 与算力网络的专著。在云网融合从 1.0 迈向 2.0 的关键节点,本书的…...
人工智能在智能交通中的应用:以L4级无人电动物流拖车为例
一、引言 人工智能(AI)技术的飞速发展正在深刻改变各个行业,其中智能交通领域尤为显著。从自动驾驶汽车到智能交通管理系统,AI的应用不仅提高了交通效率,还增强了安全性。本文将重点探讨L4级无人电动物流拖车技术及其在…...
【愚公系列】《高效使用DeepSeek》024-儿童教育
🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! 👉 江湖人称"愚公搬代码",用七年如一日的精神深耕技术领域,以"…...
第十六届蓝桥杯康复训练--6
题目链接:790. 数的三次方根 - AcWing题库 思路:二分,注意正负号和小数判断退出的方法(虽然正负无所谓) 代码: #include<bits/stdc.h> using namespace std;#define exs 0.00000018812716007232667…...
【QA】单件模式在Qt中有哪些应用?
单例设计模式确保一个类仅有一个实例,并提供一个全局访问点来获取该实例。在 Qt 框架中,有不少类的设计采用了单例模式,以下为你详细介绍并给出相应代码示例。 1. QApplication QApplication 是 Qt GUI 应用程序的核心类,每个 Q…...
logisim安装以及可能出现的问题
阅读提示:我这篇文章更偏向于安装出现问题的解决方案 目录 一、安装步骤 二、安装问题 1、出错的问题 2、出错的原因与解决方法 一、安装步骤 1、下载logisim 官方网站:https://sourceforge.net/projects/circuit/ 下载适用于你操作系统的版本&…...
Servlet、HttpServletRequest、HttpServletResponse、静态与动态网页、jsp、重定向与转发
DAY15.2 Java核心基础 JavaWeb 要想通过浏览器或者客户端来访问java程序,必须通过Servlet来处理 没有Servlet,java是无法处理web请求的 Web交互: 接收请求HttpServletRequest:可以获取到请求的信息,比如uri&#…...
2300年直线公理使数学一直存在尖锐自相矛盾
2300年直线公理使数学一直存在尖锐自相矛盾 黄小宁 复平面z各点z的对应点2z的全体是2z平面。z面拉伸(平移)变换为2z面(z2面)就使x轴⊂z面沿本身拉伸(平移)变换为u2x轴(ux2轴)。R可…...
hackmyvm-Icecream
arp-scan -l nmap -sS -v 192.168.222.106 enum4linux 192.168.222.106 445端口 smbmap -H 192.168.222.106 icecream为只读模式 smbclient \\192.168.222.106\icecream 反弹shell(上传put php-reverse-shell.php) 开启监听 nc -lnvp 1234 拿到webshell cat /etc/passwd 9000端…...
Apache Tomcat漏洞公开发布仅30小时后即遭利用
近日,Apache Tomcat曝出一项安全漏洞,在公开发布概念验证(PoC)仅30小时后,该漏洞即遭到攻击者利用。这一漏洞编号为CVE-2025-24813,主要影响以下版本: 1. Apache Tomcat 11.0.0-M1 至 11.0.2 …...
告别低效人工统计!自动计算计划进度
实时监控任务进度一直是项目管理中的一项巨大挑战。 人工统计方式不仅耗时耗力,而且往往由于信息传递的延迟和人为误差,导致无法实时获得准确的项目进展信息。 这种不准确性可能掩盖潜在的风险点,从而影响项目的整体进度和成果。 Ganttable …...
AI比人脑更强,因为被植入思维模型【16】反脆弱
毛选中就有言,不经历困难,我们就不会掌握战胜困难的方法。 这个世界纷繁复杂,不是强者总是运气好,而是他们能够失败后快速复原,不断找到战胜困难的方法。 定义 马斯洛需求层次模型是一种将人类需求从低到高按层次进…...
L2TP实验
放开安全策略机制,FW1不配IP [FW1]firewall zone trust [FW1-zone-trust]add interface GigabitEthernet 1/0/0 [FW1]security-policy [FW1-policy-security]default action permit FW2 和FW3 [FW2]interface g1/0/1 [FW2-GigabitEthernet1/0/1]ip address 2…...
【数据预测】基于遗传算法GA的LSTM光伏功率预测 GA-LSTM光伏功率预测【Matlab代码#91】
文章目录 【可更换其他算法,获取资源请见文章第6节:资源获取】1. 遗传算法GA2. 长短期记忆网络LSTM3. 基于GA-LSTM的光伏功率预测4. 部分代码展示5. 运行结果展示6. 资源获取 【可更换其他算法,获取资源请见文章第6节:资源获取】 …...
【记录一下】LMDeploy学习笔记及遇到的问题
LMDeploy 是一个用于大型语言模型(LLMs)和视觉-语言模型(VLMs)压缩、部署和服务的 Python 库。 其核心推理引擎包括 TurboMind 引擎和 PyTorch 引擎。前者由 C 和 CUDA 开发,致力于推理性能的优化,而后者纯…...
Ciura序列
一 概述 Ciura序列是一种用于希尔排序(Shell Sort)的高效增量序列。 由Marcin Ciura于2002年通过实验提出。 1)经验证最优的初始序列为:[1, 4, 10, 23, 57, 132, 301, 701] 2) 后续增量可通过最后一个元素乘以2.25生成(如:701*2.25=1577,1577*2.25=3548...)。 3)时…...
一、MySQL8的my.ini文件
MySQL8.0.11的安装版本my.ini配置文件默认存放在:C:/Program Files/MySQL/MySQL Server 8.0/ 目录下;而MySQL8.0.11绿色免安装版本是没有my.ini配置文件,用户可以自行构建后,再通过my.ini进行数据库的相关配置 一、MySQL8.0.11默…...
【贝叶斯定理(Bayesian Theorem)】
贝叶斯定理(Bayesian Theorem)是概率论中一个革命性的工具,它将主观信念与客观数据结合,形成了独特的贝叶斯统计体系。以下我们将从数学原理、哲学内涵、实际应用三个维度进行深度解析。 一、贝叶斯定理的数学本质 1. 核心公式的…...
HC-05与HC-06蓝牙配对零基础教程 以及openmv识别及远程传输项目的概述
这个是上一年的项目,之前弄得不怎么完整,只有一个openmv的,所以openmv自己去我主页找,这篇主要讲蓝牙 这个是我在使用openmv连接单片机1然后单片机1与单片机2通过蓝牙进行通信 最终实现的效果是:openmv识别到图形和数…...
如何在1分钟内编写Cursorrules
如何在1分钟内编写Cursorrules:Cursor AI用户的快速指南 编写Cursor AI的.cursorrules文件并不需要花费太多时间或显得复杂。无论你是希望定制AI编码助手的开发者,还是想确保团队编码标准一致,你都可以在短短一分钟内创建一个有效的.cursorr…...
Linux中mutex机制
在Linux中,mutex是一种用于多线程编程的同步机制,用于保护共享资源,防止多个线程同时访问或修改这些资源,从而避免竞态条件的发生。mutex 是“mutual exclusion”的缩写,意为“互斥”。 1. Mutex 的基本概念 互斥锁&…...
Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预测
Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预测 目录 Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量回归预…...
AMD公司
本文来自腾讯元宝 AMD(Advanced Micro Devices, Inc.)(先进的微型计算机设备)是一家全球领先的半导体公司,成立于1969年,总部位于美国加利福尼亚州圣克拉拉。AMD 主要从事设计、开发和销售计算机处理器、图…...
数字证书 与 数字签名 介绍
目录 数字签名 什么时候公钥加密数据,什么时候私钥加密数据? 消息认证码(MAC)和数字签名 区别 数字证书 如何使用数字证书验证服务器身份? 数字签名 定义:它类似于现实生活中的手写签名。 手写签名的法律…...
通过webrtc+canvas+css实现简单的电脑滤镜拍照效果
这里我们用的是webrtc中的MediaDevices.getUserMedia()的浏览器api进行的效果实现,MediaDevices.getUserMedia() 会提示用户给予使用媒体输入的许可,媒体输入会产生一个MediaStream,里面包含了请求的媒体类型的轨道。此流可以包含一个视频轨道…...
OpenLayers集成天地图服务开发指南
以下是一份面向GIS初学者的OpenLayers开发详细教程,深度解析代码: 一、开发环境搭建 1.1 OpenLayers库引入 <!-- 使用CDN引入最新版OpenLayers --> <link rel"stylesheet" href"https://cdn.jsdelivr.net/npm/ollatest/ol.c…...
VBA-Excel
VBA 一、数据类型与变量 常用数据类型: Byte:字节型,0~255。Integer:整数型,用于存储整数值,范围 -32768 到 32767。Long:长整型,可存储更大范围的整数,范围 -214748364…...
OpenHarmony 开源鸿蒙北向开发——linux使用make交叉编译第三方库
这几天搞鸿蒙,需要编译一些第三方库到鸿蒙系统使用。 头疼死了,搞了一个多星期总算搞定了。 开贴记坑。 一、SDK下载 1.下载 在linux下使用命令 wget https://cidownload.openharmony.cn/version/Master_Version/OpenHarmony_5.1.0.54/20250313_02…...
