问题背景近期测试中,群里发现几年前开发的说过事务死锁业务流程申请模块在频繁操作时会出现异常提示,导致审批流程失败。百遍最初以为是长到别代码逻辑不周或异常处理不足等常见错误,但通过日志排查后发现,问题问题源自数据库的还被死锁。以下是人遇日志信息: 复制SQL: UPDATE record_process_audit_apply_main_data SET update_account=?, update_name=?, update_time=?, task_node=?,apply_time=? WHERE (task_id = ?) ### Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction ; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:271) at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:70) at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:91) at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:441) at com.sun.proxy.$Proxy174.update(Unknown Source) at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:288) at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:64) at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148) at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89) at com.sun.proxy.$Proxy603.update(Unknown Source) at sun.reflect.GeneratedMethodAccessor723.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:198) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) at com.sun.proxy.$Proxy604.update(Unknown Source) at com.ifly.pdm.impl.recordaudittask.RecordProcessAuditTaskServiceImpl.handleCreateAuditTableData(RecordProcessAuditTaskServiceImpl.java:186)1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.根据日志可以清晰的看到一个事务在获取锁时发生了死锁。 分析 MySQL 错误日志进一步分析 MySQL 错误日志,再样发现以下死锁情况: 
 MySQL 最终决定回滚事务 2(事务 ID: 6536206)以解决死锁问题。 死锁原因死锁发生的问题原因在于: 事务 1 和事务 2 都在尝试更新 record_process_audit_apply_main_data 表中的记录。事务 1 正在等待事务 2 持有的还被锁,事务 2 也在等待事务 1 持有的人遇锁,导致了死锁。定位代码通过进一步分析代码,发现以下关键方法:submitApplication。该方法及其调用的其他方法都在同一个事务中执行,可能导致事务时间过长,增加了锁竞争和死锁的风险。核心代码如下所示: 
 
 
 
 优化思路:拆分事务:将大事务拆分成多个小事务,每个事务只处理一部分逻辑。确保每个事务尽可能短,减少锁持有时间。异步处理:使用异步任务处理耗时操作,亿华云计算如调用第三方接口。使用消息队列将部分操作异步化。优化数据库操作:确保所有涉及的表都有适当的索引,减少查询和更新的时间。使用批量操作,减少与数据库的交互次数。事务隔离级别:根据业务需求选择合适的事务隔离级别,减少锁的竞争。日志记录:记录事务的开始和结束时间,以及关键操作的执行情况,便于问题排查。优化后的伪代码: 复制@Transactional public void submitApplication(Application application) { // 拆分事务,确保每个事务尽量短 try { processApplicationDetails(application); // 处理申请细节 updateTaskStatus(application); // 更新任务状态 notifyUser(application); // 通知用户 } catch (Exception e) { log.error("Error in submitApplication", e); throw new RuntimeException("Application submission failed"); } } // 异步处理耗时任务 @Async public void notifyUser(Application application) { // 异步通知用户 notificationService.sendNotification(application.getUserId(), "Your application is processed."); }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.服务器租用 |