现象
PS:线上日志突然曝出死锁问题
Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
The error may involve com.aihuishou.opt.wallet.dao.ServiceChargeMapper.updateProcessByFeeTransactionId-Inline
The error occurred while setting parameters
SQL: update t_service_charge SET fee_transaction_id = ?, status = ?, completed_dt = ? where fee_transaction_id = ? and status = 1
Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
; SQL []; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
分析过程:
-
1.分析代码是否并发问题
代码如下
Account platformAccount = accountMapper.selectAccountForUpdate(fundFlowDetail.getInAccountId()); //省略一些其他的业务代码 ServiceCharge updateServiceCharge = new ServiceCharge(); updateServiceCharge.setCompletedDt(cDate); updateServiceCharge.setStatus(GeneralStatusEnum.SUCCESS.getValue()); updateServiceCharge.setFeeTransactionId(sc.getFeeTransactionId()); int rd = serviceChargeMapper.updateProcessByFeeTransactionId(updateServiceCharge); //曝出死锁问题的语句
PS:该方法在一个事物里面,事物隔离级别为RC,因为使用了selectAccountForUpdate 所以能够排除同一个单据并发问题
-
2.查看SQL语句
- 有问题的SQL已经在异常里面给出
update t_service_charge SET fee_transaction_id = ?, status = ?, completed_dt = ? where fee_transaction_id = ? and status = 1
- 查看该表对应的索引
PRIMARY KEY (`id`), UNIQUE KEY `uk_fee_transaction_id` (`fee_transaction_id`), UNIQUE KEY `uk_detailid_channel_bustype` (`business_type`,`channel_type`,`detail_id`), KEY `idx_update_dt` (`update_dt`), KEY `idx_create_dt` (`create_dt`), KEY `idx_completed_dt` (`completed_dt`), KEY `idx_transaction_id` (`transaction_id`), KEY `idx_account_tradetype` (`account_id`,`trade_type`), KEY `idx_trade_type_feetype` (`trade_type`,`fee_type`) USING BTREE, KEY `idx_status` (`status`)
PS:通过该SQL与表上的索引,自己又在测试环境验证了一下.发现测试环境给出的执行计划该语句只使用了uk_fee_transaction_id(唯一索引),自己陷入死循环
-
3.求助DBA
通过和DBA商讨,让DBA在线上抓取SQL并分析执行计划,该执行计划如下
PS:发现线上该SQL的执行计划于自己在线下分析的并不一致,线上mysql执行的时候选择索引居然是两个,uk_fee_transaction_id 和 idx_status,问题就出现在idx_staus 上面
-
解决办法
-
1.update 的时候使用 force index,强制语句只走唯一索引
-
2.更新SQL语句,使用主键去更新
-