news 2026/4/18 11:54:12

真实业务场景死锁案例:电商订单处理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
真实业务场景死锁案例:电商订单处理

1. 业务场景介绍

场景:电商系统的订单确认流程,需要处理三个核心资源:

  1. 订单锁:防止同一订单被重复处理
  2. 库存锁:防止库存超卖
  3. 支付锁:防止重复支付

2. 死锁发生的真实代码

2.1 订单处理服务

@ServicepublicclassOrderService{@AutowiredprivateRedissonClientredissonClient;@AutowiredprivateInventoryServiceinventoryService;@AutowiredprivatePaymentServicepaymentService;/** * 确认订单(路径1:先锁订单,再锁库存,最后锁支付) */publicvoidconfirmOrder(StringorderId,StringproductId,StringuserId){// 1. 锁定订单RLockorderLock=redissonClient.getLock("order:lock:"+orderId);orderLock.lock();// 线程A持有订单锁try{// 2. 锁定库存RLockinventoryLock=redissonClient.getLock("inventory:lock:"+productId);inventoryLock.lock();// 线程A持有库存锁try{// 3. 锁定支付RLockpaymentLock=redissonClient.getLock("payment:lock:"+userId);paymentLock.lock();// 线程A请求支付锁try{// 业务逻辑:确认订单、扣减库存、创建支付记录doConfirmOrder(orderId,productId,userId);}finally{paymentLock.unlock();}}finally{inventoryLock.unlock();}}finally{orderLock.unlock();}}/** * 快速确认订单(路径2:先锁支付,再锁库存,最后锁订单) */publicvoidquickConfirmOrder(StringorderId,StringproductId,StringuserId){// 1. 锁定支付(与confirmOrder顺序不同)RLockpaymentLock=redissonClient.getLock("payment:lock:"+userId);paymentLock.lock();// 线程B持有支付锁try{// 2. 锁定库存RLockinventoryLock=redissonClient.getLock("inventory:lock:"+productId);inventoryLock.lock();// 线程B持有库存锁try{// 3. 锁定订单(与confirmOrder顺序不同)RLockorderLock=redissonClient.getLock("order:lock:"+orderId);orderLock.lock();// 线程B请求订单锁try{// 业务逻辑:快速确认订单、扣减库存、创建支付记录doQuickConfirmOrder(orderId,productId,userId);}finally{orderLock.unlock();}}finally{inventoryLock.unlock();}}finally{paymentLock.unlock();}}// 其他方法...}

3. 死锁发生的详细流程

3.1 前置条件

  • 线程A:处理用户请求,调用confirmOrder()方法
  • 线程B:处理同一用户的另一个请求,调用quickConfirmOrder()方法
  • 共享资源
    • 订单ID:order_123
    • 商品ID:product_456
    • 用户ID:user_789

3.2 执行时序图

时间点线程A线程B资源状态
T0调用confirmOrder("order_123", "product_456", "user_789")调用quickConfirmOrder("order_123", "product_456", "user_789")所有锁都未被持有
T1获取order:lock:order_123(成功)订单锁被线程A持有
T2获取payment:lock:user_789(成功)订单锁被A持有,支付锁被B持有
T3获取inventory:lock:product_456(成功)订单锁被A持有,支付锁被B持有,库存锁被A持有
T4获取inventory:lock:product_456(等待中…)线程B等待库存锁
T5获取payment:lock:user_789(等待中…)线程A等待支付锁,线程B等待库存锁
T6+无限等待支付锁无限等待库存锁死锁发生!

3.3 死锁的具体表现

现象影响
线程A状态卡在paymentLock.lock()处,无法继续执行
线程B状态卡在inventoryLock.lock()处,无法继续执行
资源状态订单锁、库存锁被A持有;支付锁被B持有
系统表现两个订单请求都无法完成,超时后返回错误
监控表现线程池线程逐渐耗尽,系统响应变慢

4. 死锁的根本原因

4.1 死锁的四个必要条件都满足

条件具体表现
互斥条件每个锁只能被一个线程持有
请求与保持条件线程A持有订单锁和库存锁,同时请求支付锁;线程B持有支付锁,同时请求库存锁
不剥夺条件锁不能被强制剥夺,只能由持有线程主动释放
循环等待条件线程A → 支付锁(被B持有);线程B → 库存锁(被A持有),形成循环等待链

4.2 业务设计缺陷

  1. 锁获取顺序不一致confirmOrder()quickConfirmOrder()两个方法的锁获取顺序不同
  2. 嵌套锁使用不当:同时使用多个嵌套锁,增加了死锁风险
  3. 缺乏超时机制:使用lock()而非tryLock(),导致无限等待
  4. 锁粒度不够细:支付锁按用户ID加锁,可能导致同一用户的所有请求串行化

5. 解决方案:如何避免死锁

5.1 方案1:统一锁获取顺序(推荐)

核心原则所有线程必须按照相同的顺序获取锁

5.1.1 优化后的代码
@ServicepublicclassOrderService{// 定义全局锁顺序:订单锁 → 库存锁 → 支付锁privatestaticfinalList<String>LOCK_ORDER=Arrays.asList("order:lock:","inventory:lock:","payment:lock:");/** * 确认订单(统一锁顺序) */publicvoidconfirmOrder(StringorderId,StringproductId,StringuserId){// 1. 收集所有需要的锁List<String>lockKeys=Arrays.asList("order:lock:"+orderId,"inventory:lock:"+productId,"payment:lock:"+userId);// 2. 按统一顺序排序锁List<String>sortedLockKeys=sortLocks(lockKeys);// 3. 按顺序获取锁List<RLock>acquiredLocks=newArrayList<>();try{for(StringlockKey:sortedLockKeys){RLocklock=redissonClient.getLock(lockKey);lock.lock();acquiredLocks.add(lock);}// 4. 执行业务逻辑doConfirmOrder(orderId,productId,userId);}finally{// 5. 按相反顺序释放锁Collections.reverse(acquiredLocks);for(RLocklock:acquiredLocks){lock.unlock();}}}/** * 快速确认订单(同样的锁顺序) */publicvoidquickConfirmOrder(StringorderId,StringproductId,StringuserId){// 使用完全相同的锁顺序,避免死锁confirmOrder(orderId,productId,userId);}/** * 按预定义顺序排序锁 */privateList<String>sortLocks(List<String>lockKeys){returnlockKeys.stream().sorted(Comparator.comparing(key->{// 根据锁前缀获取排序值for(inti=0;i<LOCK_ORDER.size();i++){if(key.startsWith(LOCK_ORDER.get(i))){returni;}}returnInteger.MAX_VALUE;})).collect(Collectors.toList());}}

5.2 方案2:使用tryLock替代lock

核心原则避免无限等待,设置合理的超时时间

publicvoidconfirmOrder(StringorderId,StringproductId,StringuserId){RLockorderLock=redissonClient.getLock("order:lock:"+orderId);RLockinventoryLock=redissonClient.getLock("inventory:lock:"+productId);RLockpaymentLock=redissonClient.getLock("payment:lock:"+userId);try{// 尝试获取订单锁,最多等待3秒if(!orderLock.tryLock(3,TimeUnit.SECONDS)){thrownewRuntimeException("获取订单锁失败");}try{// 尝试获取库存锁,最多等待3秒if(!inventoryLock.tryLock(3,TimeUnit.SECONDS)){thrownewRuntimeException("获取库存锁失败");}try{// 尝试获取支付锁,最多等待3秒if(!paymentLock.tryLock(3,TimeUnit.SECONDS)){thrownewRuntimeException("获取支付锁失败");}try{doConfirmOrder(orderId,productId,userId);}finally{paymentLock.unlock();}}finally{inventoryLock.unlock();}}finally{orderLock.unlock();}}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewRuntimeException("获取锁被中断");}}

5.3 方案3:减少锁的嵌套(推荐)

核心原则最小化锁的使用,避免嵌套锁

@ServicepublicclassOrderService{/** * 优化后:减少锁的嵌套,降低死锁风险 */publicvoidconfirmOrder(StringorderId,StringproductId,StringuserId){// 1. 先检查库存(无锁,快速失败)if(!inventoryService.checkInventory(productId)){thrownewRuntimeException("库存不足");}// 2. 锁定订单(最核心的锁)RLockorderLock=redissonClient.getLock("order:lock:"+orderId);try{if(!orderLock.tryLock(5,30,TimeUnit.SECONDS)){thrownewRuntimeException("获取订单锁失败");}// 3. 再次检查库存(双重检查,防止并发更新)if(!inventoryService.checkInventory(productId)){thrownewRuntimeException("库存不足");}// 4. 扣减库存(原子操作,数据库乐观锁)inventoryService.deductInventory(productId);// 5. 创建支付记录(幂等设计,无需锁)paymentService.createPaymentRecord(orderId,userId);// 6. 确认订单doConfirmOrder(orderId,productId,userId);}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewRuntimeException("获取锁被中断");}finally{orderLock.unlock();}}}

6. 死锁监控与排查工具

6.1 生产环境监控

工具用途关键指标
JMX监控JVM线程状态线程数量、阻塞线程数、死锁检测
SkyWalking分布式链路追踪慢调用、超时调用、异常调用
Prometheus + Grafana系统监控线程池使用率、响应时间、错误率
Redisson监控分布式锁监控锁获取次数、锁等待时间、锁持有时间

6.2 死锁排查命令

# 1. 查看JVM线程状态,检测死锁jstack-l<PID>|grep-A20"Found one Java-level deadlock"# 2. 查看线程详情jstack<PID>>jstack.txt# 分析jstack.txt文件,寻找BLOCKED状态的线程# 3. 使用VisualVM图形化工具jvisualvm# 启动VisualVM,连接到JVM进程,查看线程状态

7. 总结

7.1 死锁的本质

死锁的本质是资源的无序竞争+无限等待

7.2 真实业务场景的死锁预防

最佳实践效果
统一锁获取顺序打破循环等待条件
使用tryLock替代lock避免无限等待
最小化锁持有时间减少死锁发生概率
减少锁的嵌套降低死锁复杂度
实现幂等设计减少对锁的依赖
定期检测死锁及时发现和处理死锁

7.3 最终建议

库存扣减等核心业务场景中:

  1. 优先使用数据库乐观锁,减少对分布式锁的依赖
  2. 使用细粒度锁,按商品ID/订单ID加锁
  3. 设置合理的tryLock参数,避免无限等待
  4. 统一锁获取顺序,避免循环等待
  5. 实现幂等性,确保重复执行不会产生副作用

通过以上措施,可以有效避免真实业务场景中的死锁问题,确保系统的高可用性和可靠性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/9 22:14:04

STM32 HAL 函数大全 · 第 3 卷

TIM 定时器模块&#xff08;基础定时 / PWM / 输入捕获 / 输出比较&#xff09; 版本&#xff1a;v1.1 | 适用系列&#xff1a;STM32F0 / F1 / F3 / F4 / F7 / L0 / L4 / H7 1. TIM 模块简介与分类 定时器&#xff08;Timer&#xff09;是 STM32 中功能最复杂、应用最广泛的外…

作者头像 李华
网站建设 2026/4/18 8:35:33

计算机深度学习毕设实战-基于卷神经网络深度学习识别水果的成熟度

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/17 12:37:07

面向新手的鸿蒙跨平台开发技术选型指南

&#x1f4a1; 一、为什么要关注“跨平台 鸿蒙” 对于刚入门移动 / 多端开发的同学&#xff0c;现在面临的现实是&#xff1a; 安卓 / iOS 依然是主战场&#xff1b;Web、桌面、小程序、IoT 等场景越来越多&#xff1b;鸿蒙&#xff08;OpenHarmony / HarmonyOS&#xff09;…

作者头像 李华
网站建设 2026/4/17 17:53:44

楼宇设备运维标准规范:以标准化体系提升物业运维能力

设备运维标准的核心框架楼宇设备运维是物业运营的核心支撑&#xff0c;其标准化体系直接决定了物业对设施的管控精度与服务输出质量。设备运维标准体系需覆盖全生命周期管理要求&#xff0c;包括设备分类编码、日常巡检流程、故障处置规范、维护记录管理等核心模块。分类编码是…

作者头像 李华
网站建设 2026/4/18 8:42:39

吐血推荐10个AI论文写作软件,MBA论文写作必备!

吐血推荐10个AI论文写作软件&#xff0c;MBA论文写作必备&#xff01; AI 工具正在重塑论文写作的未来 在当今学术研究日益数字化的背景下&#xff0c;AI 工具已经成为许多 MBA 学生和研究人员不可或缺的助手。尤其是在论文写作过程中&#xff0c;AI 不仅能够帮助提高效率&…

作者头像 李华