update 鎖表經(jīng)驗分享(1)

先貼錯誤異常:

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或其他方式,就可以避免鎖表的問題。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容