java数据库事务死锁_【数据库事务】-应用数据库Deadlock问题排查

本文介绍了在【Flow-center-web】系统中遇到的数据库死锁问题,通过CAT监控和MySQL死锁状态分析,发现死锁源于流程创建和更新消息并发消费,以及代码中消息Partition路由策略失效。解决方案包括修复消息路由策略和调整数据库事务操作顺序,以确保消息处理的顺序性和避免资源冲突。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

清流控制台应用系统【Flow-center-web】在【2020-08-07 16:40:XX】-【2020-08-07 17:40:XX】期间陆续收到【数据库Deadlock】Cat告警消息,

需要排查和定位问题的原因,

检查应用系统是否存在缺陷风险

此问题对应用系统的影响范围。

问题的解决方案

378ea9d33abca497dd5ec14e7cd68a3e.png

问题排查

CAT监控排查

通过应用系统的Cat监控,发现【2020-08-07 16:40:XX】-【2020-08-07 17:40:XX】应用系统打打点流程的处理失败率较高,具体如图

cd5a28c50bf4a5383623c563ac5e86e9.png

详情参考:

2cb0e8982f87af494cf067fb1305b1c6.png

f6fe136902601af41d4822e81b4657ad.png

通过Cat监控大批量的出现了【DeadlockLoserDataAccessException】,此异常调用链主要时在清流流程处理监控消息时【processWFMsg】来根据流程的状态信息决定创建流程【createFlow】或者时更新流程【updateFlow】的行为.

e11719f6c5db2b5be2e7a13a81d3972d.png

死锁状态分析

['

=====================================

2020-08-07 16:55:12 0x7f5ead17d700 INNODB MONITOR OUTPUT

=====================================

Per second averages calculated from the last 8 seconds

-----------------

BACKGROUND THREAD

-----------------

srv_master_thread loops: 4896283 srv_active, 0 srv_shutdown, 265242 srv_idle

srv_master_thread log flush and writes: 5161498

----------

SEMAPHORES

----------

OS WAIT ARRAY INFO: reservation count 41499456

OS WAIT ARRAY INFO: signal count 54386950

RW-shared spins 0, rounds 39950592, OS waits 4200543

RW-excl spins 0, rounds 2396693258, OS waits 18732934

RW-sx spins 48873547, rounds 999958857, OS waits 15310886

Spin rounds per wait: 39950592.00 RW-shared, 2396693258.00 RW-excl, 20.46 RW-sx

------------------------

LATEST DETECTED DEADLOCK

------------------------

2020-08-07 16:55:12 0x7f49dd59b700

*** (1) TRANSACTION:

TRANSACTION 371818041, ACTIVE 0 sec inserting

mysql tables in use 1, locked 1

LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 2

MySQL thread id 3412760, OS thread handle 139955255465728, query id 2953157849 10.72.140.11 flowcenter update

insert into wf_node

( flow_id,

node_name,

node_duration,

node_wait_duration,

node_status,

node_reentry_times,

node_id,

node_result )

values ( 1291659133342281834,

\'start\',

0,

0,

0,

0,

1291659133342281813,

\'{"status":"SUCCESS","message":"","success":true}\' )

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 54 page no 11412699 n bits 528 index IX_NodeId of table `FlowCenter`.`wf_node` trx id 371818041 lock_mode X locks gap before rec insert intention waiting

*** (2) TRANSACTION:

TRANSACTION 371818040, ACTIVE 0 sec starting index read

mysql tables in use 1, locked 1

6 lock struct(s), heap size 1136, 3 row lock(s)

MySQL thread id 3412884, OS thread handle 139955222984448, query id 2953157856 10.76.195.174 flowcenter updating

update wf_flow

SET template_id = 446,

start_time = \'2020-08-07 16:55:10\',

duration = 2489,

wait_duration = 0,

flow_status = 2,

current_node_id = 1291659133342281813,

current_step_id = 1291659133342281812,

priority = 0

WHERE ( flow_id = 1291659133342281834 )

*** (2) HOLDS THE LOCK(S):

RECORD LOCKS space id 54 page no 11412699 n bits 528 indexIX_NodeId of table `FlowCenter`.`wf_node` trx id 371818040 lock_mode X locks gap before rec

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 51 page no 480250 n bits 744 index IX_FlowId of table `FlowCenter`.`wf_flow` trx id 371818040 lock_mode X waiting

通过Mysql的死锁的状态Dump日志分析发现,Mysql分别开启2个事务执行流程操作,分别时【trx id 371818041】的 Insert 【wf_node】与【trx id 371818040】的update【wf_flow】,由于2个事务间存在相互锁定资源,从而引起死锁问题,至于死锁的原因待后续图解,但是根据应用系统处理的特性,流程创建事务操作的执行链如下:

createFlow

|

|-------------insert wf_flow

|-------------insert wf_node

|-------------insert wf_step

updateFlow

|-------------update wf_node

|-------------update wf_step

|-------------update wf_flow

应用排查

消息处理记录排查

基本上通过上面的流程事务处理执行链,很明显存在一个资源竞争的顺序问题,也是大多数死锁产生的原因,但是需要一个前提,那就是事务并发的情况,基础此,排查一下,应用系统中对于createFlow与updateFlow中并发操作的可能行进行排查,通过系统记录的Mafka消息消费记录表中的记录发现,的确发现了流程的流程创建消息和流程更新消息并发的消费了。

0802f0b6f69365ff086395da0e22f522.png

但是清流应用系统定制了Mafka的消息发送Partition的路由策略,能保证对于同一个流程(FlowId相同)的消息处理,会发送到同一个partition,同时对于partition的消息也是按照串行的方式,但是通过监控消息表中的消费记录,发现并未有按照预期的设想处理,由此需要从源头中排查2个问题,

1.清流定制的消息Partition的路由策略为何没有生效? 是中间件问题,还是应用代码实现机制有问题?

2.假设消息Partition的路由策略时效,消费并发处理,是否意味着消息发送的并发处理,还是意味着消息积压导致?

消息发送记录排查

1.消息Partition的路由策略失效排查

通过代码排查发现清流定制的消息Partition的路由策略代码没有在最新的代码中实现,对比代码修改记录,在清流1.2.26版本之后,此段Partition的路由策略代码也就遗漏,因此需要修复此代码缺陷问题。

964d2ca5d07243efce23cbd135e3335b.png VS 

6651c31d2bf258327d65103011c90241.png

2.消费消息并发处理问题排查

由于流程消息发送时,清流1.2.26版本之后,此段Partition的路由策略失效,导致同一个流程Flowid可能被发送到多个partition的缘故,在多个partition中的消息消费无法保证消息处理的顺序性,因此存在并发处理的可能性

消息创建和消息更新的消息发送到不同的partition中的offset差异,对于不同的partition,可能由于partition积压时,也会存在并发消息问题,本质还是消息的顺序性问题

问题定位

通过上面的问题排查,

1.定位出数据库死锁的根本原因是由于Mafka流程创建消息和Mafka流程更新消息并发消费顺序问题

2.在应用程序代码中流程创建的资源锁定和流程更新的资源锁定顺序不一致,从而导致数据库的资源的相互锁定问题

createFlow

|

|-------------insert wf_flow

|-------------insert wf_node

|-------------insert wf_step

updateFlow

|-------------update wf_step

|-------------update wf_node

|-------------update wf_flow

解决方案

消息Partition的路由策略失效

修改清流1.2.26版本之后,按照FlowId路由策略来发送消息,确保消息的顺序性问题

数据库事务操作的顺序性问题

调整更新操作的顺序性问题 createFlow

|

|-------------insert wf_flow

|-------------insert wf_node

|-------------insert wf_step

updateFlow

|-------------update wf_flow

|-------------update wf_node

|-------------update wf_step

问题验证

先忽略数据库中的GAP锁中复杂情况,单独从执行顺序角度来复现问题验证一下

8447303baeb3221bc4c36ad8aaab4ce0.png

复现脚本

---------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------|

begin ; |

insert into wf_flow(flow_id,template_id,current_node_id,current_step_id) values(4,443,40,400); | begin;

| update wf_node set node_name='n1' where node_id=40;

| update wf_step set step_name='s1',node_id=30 where step_id=300;

insert into wf_node(flow_id,node_id) values(4,40); -----query waiting...... |

| update wf_flow set current_node_id=40,current_step_id=400 where flow_id=4;

| -----Deadlock found when trying to get lock; try restarting transaction

insert into wf_step(flow_id,node_id,step_id) values(4,40,400);//never happend due deadlock |

<think>好的,我现在需要处理用户关于Java死锁排查方法和解决方案的查询。首先,我要回顾一下用户提供的系统级指令和之前的引用内容,确保回答符合要求。 用户明确要求用中文回答,并且要结构清晰,分步骤解决问题。同时,用户提到了几个引用,我需要参考这些引用中的信息,尤其是引用[1]、[2]、[3]、[4]、[5]中有关Java死锁的内容。 首先,死锁的定义:多个进程或线程因竞争资源而互相等待,无法继续执行。根据引用[1][2][3],这一点已经明确。接下来,排查方法部分,用户需要具体的步骤。根据引用[1]提到的,Java死锁排查可以使用jstack工具,而引用[2]和[3]也提到了类似的方法,比如线程转储和分析日志。同时,引用[4]提到了锁排序,引用[5]虽然主要讲数据库死锁,但部分思路可能适用,比如调整隔离级别和优化事务逻辑,但这里需要专注于Java多线程的情况。 解决方案方面,引用[4]详细说明了锁排序,避免环路等待,而引用[2]和[3]也提到了预防策略,如锁顺序、超时机制等。需要将这些方法组织起来,分点说明。 另外,用户要求生成相关问题,我需要根据内容提出相关的问题,比如排查工具的其他方法、预防策略、与数据库死锁的区别等。 需要注意,回答中引用的部分要自然添加标识,比如[^1][^2]等,但用户提供的引用可能不完全对应,需要根据实际情况调整。例如,引用[4]提到锁排序,引用[2]提到预防胜于治疗,这些都需要正确标注。 最后,代码示例部分,用户可能希望看到如何模拟死锁以及解决方案的代码。根据引用中的示例,需要确保代码正确,比如使用synchronized关键字,并展示正确的锁顺序。 现在需要将这些点整合成一个结构清晰的回答,分步骤说明排查方法和解决方案,并添加相关问题。确保使用正确的中文术语,LaTeX格式正确,行内公式用$...$,独立公式用$$...$$,但在这个问题中可能数学公式较少,主要关注代码和文本结构。</think>### Java死锁排查方法与解决方案指南 #### 一、死锁形成条件(需同时满足) 1. 互斥条件:资源独占(如`synchronized`锁) 2. 请求与保持:持有旧锁请求新锁 3. 不可剥夺:锁只能主动释放 4. 环路等待:多个线程形成环形等待链 #### 二、排查方法(4种实战手段) 1. **jstack工具分析** ```bash jstack <PID> > thread_dump.txt ``` 搜索"deadlock"关键词,定位死锁线程[^1][^3] 2. **JConsole可视化检测** - 连接Java进程后查看"线程"标签 - 检测带有红色警告的线程 3. **代码示例(典型死锁场景)** ```java // 错误示例 Thread1: 锁定A后请求B Thread2: 锁定B后请求A ``` 4. **线程转储分析** ```java // 编程式获取线程信息 ThreadMXBean bean = ManagementFactory.getThreadMXBean(); long[] threadIds = bean.findDeadlockedThreads(); ``` #### 三、解决方案(5种核心策略) 1. **统一加锁顺序** ```java // 正确示例:全局定义锁顺序 private static final Object lock1 = new Object(); private static final Object lock2 = new Object(); void method() { synchronized(lock1) { synchronized(lock2) { /* 操作 */ } } } ``` 通过预定义顺序消除环路等待[^4] 2. **设置锁超时机制** ```java ReentrantLock lock = new ReentrantLock(); if (lock.tryLock(300, TimeUnit.MILLISECONDS)) { try { /* 临界区 */ } finally { lock.unlock(); } } ``` 3. **开放调用模式** - 避免在持有锁时调用外部方法 - 缩小同步代码块范围 4. **死锁检测系统** ```java ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(this::checkDeadlock, 5, 10, SECONDS); ``` 5. **资源分级策略** - 建立锁的层次结构 - 高层锁可请求低层锁,反之禁止 #### 四、预防建议(3个维度) 1. **设计阶段** - 使用`java.util.concurrent`高级API - 优先选择无锁数据结构 2. **编码规范** - 单次最多获取一个锁 - 同步块内代码执行时间<1ms 3. **监控体系** - 集成APM工具(如SkyWalking) - 设置线程池监控指标 ```mermaid graph TD A[死锁发生] --> B{jstack分析} B --> C[定位死锁线程] C --> D[检查锁持有链] D --> E[修改锁顺序] E --> F[增加超时机制] F --> G[测试验证] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值