spring boot

update 锁表经验分享(1)

2019-04-09  本文已影响294人  燃英

先贴错误异常:

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 语句,用的是行级锁,执行起来都是毫秒级的,怎么会出锁表这么严重的问题呢?

我做了很多测试,发现没办法重现,听说是索引的问题,我就改为根据主键ID update,但最后还是没解决。
后来仔细看这个错误提示,感觉这个似曾相识:

进行悲观锁操作的时候就有啊!

BEGIN;
SELECT * FROM user_info WHERE id = 2 FOR UPDATE;

结果:


image.png

换种思路是不是别的地方锁了,然后这边update的时候出错,与这个SQL本身没关系呢?
查看了源码,发现有个定时任务果然有类似做法:

 @Transactional(rollbackFor = Exception.class)
Thread.sleep(1000);

找到问题原因: 这里开启了一个事务 并查询到了对应的数据,但是线程Sleep了,事务没有提交,此时在另外一个线程去update就会一直等待,知道抛出 Lock wait timeout exceeded。

解决办法:
去掉 @Transactional(rollbackFor = Exception.class) 方法中的 Thread.sleep 写法,换成MQ或其他方式,就可以避免锁表的问题。

上一篇下一篇

猜你喜欢

热点阅读