背景
有个定时任务特别耗时,涉及到了一个表,此时平台页面触发一个该表的操作,请求卡顿了几十秒,最后返回了一个500异常。
报错
java
### Error updating database. Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
### The error may exist in com/xxx/xxx/xxx/xxxMapper.java (best guess)
### The error may involve com.xxx.xxx.xxx.xxxMapper.update-Inline
### The error occurred while setting parameters
### SQL: UPDATE table SET update_time=?,task_status=? WHERE delete_flag=0 AND (id = ? AND task_status <= ?)
### Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
; Lock wait timeout exceeded; try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
解决
问题的关键是多个线程来竞争同一把锁导致的,InnoDB的锁默认等待时间innodb_lock_wait_timeout=50秒,
如何避免一个线程拿到锁的时间太长
便是我们的重中之重。
- 修改innodb_lock_wait_timeout的值,无疑是杯水车薪,治标不治本。
- 先根据报错日志找到这两条SQL语句的具体操作,分析是否有耗时现象,是否存在长事务问题(
加了@Transactional
,我这块就是这个原因导致的)。 - 或者执行
show engine innodb status
,查看是否有锁等待/死锁信息,都可以找到是哪条SQL导致的。 - 如果还不行,就需要
适当优化索引
了,宗旨就是减少SQL语句的执行时间。