news 2026/4/15 16:05:28

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

作者头像

张小明

前端开发工程师

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/11 10:11:12

深度学习毕设项目推荐-人工智能基于python深度学习的餐桌美食识别

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

作者头像 李华
网站建设 2026/4/6 23:55:48

Python+Vue的江西省铅山县鹅湖书院景点网站 Pycharm django flask

这里写目录标题项目介绍项目展示详细视频演示技术栈文章下方名片联系我即可~解决的思路开发技术介绍性能/安全/负载方面python语言Django框架介绍技术路线关键代码详细视频演示收藏关注不迷路&#xff01;&#xff01;需要的小伙伴可以发链接或者截图给我 项目介绍 江西省铅山…

作者头像 李华
网站建设 2026/4/12 4:26:22

西安GEO优化市场深度解析:主流服务商全景对比与选型指南

西安GEO优化市场深度解析&#xff1a;主流服务商全景对比与选型指南 在生成式AI重塑信息获取方式的今天&#xff0c;企业品牌在AI对话答案中的“可见性”已成为新的竞争壁垒。西安作为西部科技创新与产业中心&#xff0c;本地企业对GEO&#xff08;生成式引擎优化&#xff09;服…

作者头像 李华
网站建设 2026/4/15 15:04:27

AI城市管理综合执法系统:让城市治理有“智”更有“度”

传统城管执法常陷“人海战术”困境&#xff1a;人工巡查效率低、夜间漏检多、跨部门协同慢。而AI城市管理综合执法系统&#xff0c;用“技术慧眼智能大脑”重构治理逻辑&#xff0c;把被动响应变成主动预判&#xff0c;让执法既精准又有温度&#xff0c;这背后是多重技术的协同…

作者头像 李华
网站建设 2026/4/14 23:55:34

深度学习计算机毕设之基于python深度学习的餐桌美食识别卷神经网络

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

作者头像 李华