先貼錯誤異常:
org.springframework.dao.CannotAcquireLockException:
### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
### The error occurred while setting parameters
### SQL: UPDATE user_info SET replied = ? WHERE id = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
; SQL []; Lock wait timeout exceeded; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:259)
org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:73)
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:73)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:446)
at com.sun.proxy.$Proxy77.update(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:294)
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at com.mysql.jdbc.Util.getInstance(Util.java:408)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:952)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2680)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1858)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)
... 70 more
很明顯是鎖表了,按道理說表引擎為 InnoDB ,一個簡單的update 語句,用的是行級鎖,執(zhí)行起來都是毫秒級的,怎么會出鎖表這么嚴(yán)重的問題呢?
我做了很多測試,發(fā)現(xiàn)沒辦法重現(xiàn),聽說是索引的問題,我就改為根據(jù)主鍵ID update,但最后還是沒解決。
后來仔細(xì)看這個錯誤提示,感覺這個似曾相識:
進(jìn)行悲觀鎖操作的時候就有啊!
BEGIN;
SELECT * FROM user_info WHERE id = 2 FOR UPDATE;
結(jié)果:

image.png
換種思路是不是別的地方鎖了,然后這邊update的時候出錯,與這個SQL本身沒關(guān)系呢?
查看了源碼,發(fā)現(xiàn)有個定時任務(wù)果然有類似做法:
@Transactional(rollbackFor = Exception.class)
Thread.sleep(1000);
找到問題原因: 這里開啟了一個事務(wù) 并查詢到了對應(yīng)的數(shù)據(jù),但是線程Sleep了,事務(wù)沒有提交,此時在另外一個線程去update就會一直等待,知道拋出 Lock wait timeout exceeded。
解決辦法:
去掉 @Transactional(rollbackFor = Exception.class) 方法中的 Thread.sleep 寫法,換成MQ或其他方式,就可以避免鎖表的問題。