mysql update 死锁问题

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

现象

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语句,使用主键去更新

转载于:https://my.oschina.net/nixi0608/blog/3033491

你可能感兴趣的:(mysql update 死锁问题)