当前位置:   article > 正文

MySQLTransactionRollbackException:lock wait timeout exceeded_mysqltransactionrollbackexception: lock wait timeo

mysqltransactionrollbackexception: lock wait timeout exceeded; try restartin

记一次Mysql异常:

MySQLTransactionRollbackException:lock wait timeout exceeded;try restarting transaction

排查方式:

去掉spring的事务再执行,如果不报这个错误,证明就是事务引起的;

产生问题的伪代码如下,可以看出问题产生的原因:

  1. //controller中调用service的方法,为methodA开启了一个事务,假设叫 “事务1”
  2. @Controller
  3. public class AController{
  4.     public void a(){
  5.         aService.methodA();
  6. }
  7. }
  8. //methodA 中删除B表数据,
  9. public class AService{
  10.    
  11. @Transaction
  12.     public void methodA(){
  13.     /*
  14. 删除B表中的数据,"事务1" 已经获取了B表的写锁,
  15. 但是注意:"事务1" 删除完数据(methodB()执行完后),并没有提交 “事务1”, 问题就出在这里
  16. */
  17.       bService.methodB();
  18.        
  19.         /*
  20. 开启一个新线程调用methodC, 由于methodC处于另一个线程中,而spring的事务是和线程绑定的,
  21. 所以spring会为methodC开启另一个事务,假设叫 “事务2”
  22. */
  23.        Thread thread = new Thread(new Runnable(){
  24.             public void run(){
  25. /*
  26.                     “事务2”中也要对B进行写操作,即也要获取B表的写锁,但此时写锁还被 “事务1”持有,
  27. 因此methodC 无法执行,一直卡住直到超时。
  28. 超时时间由变量 “innodb_lock_wait_timeout”进行配置,可以通过
  29. show varibales like 'innodb_lock_wait_timeout' 进行查看,单位为秒
  30. */
  31.                 cService.methodC();
  32. }
  33. })
  34. thread.start();
  35.        
  36. //阻塞主线程直到
  37. thread.join();
  38.     }
  39. }
  40. public class BService{
  41. @Transaction
  42. public void methodB(){
  43. //删除B表中的数据
  44. 执行 delete from b where .....
  45. }
  46. }
  47. public class CService{
  48. @Transaction
  49. public void methodC(){
  50. //修改 B表中的数据
  51. 执行 update b set .....
  52. }
  53. }

仔细看上面代码中的注释,分析过程:

① 主线程开启了一个事务1,对b表进行写操作,写完之后事务1未提交;

② 主线程开启子线程,且 子线程不执行完,主线程一直挂起(即join的作用)

③ spring的事务是通过线程进行传递的,子线程检测不到已经开启了事务1,会开启新事务,即事务2,准备对b表进行写操作

④ 由于b表写锁还被 事务1持有,所以事务2无法获取b表写锁,事务2一直等待,直到超时

总结就是:事务存在了嵌套(两个线程各开启了一个事务),外层事务对b表进行写操作,未释放锁,内层事务也要获取写锁操作b表,无法获取,一直等待。

事务1 开始

执行 delete from b ....... ,持有b表写锁

事务2开始

执行 update b set ...... , 获取b表写锁超时

事务2结束

事务1 结束

解决办法目前想到两种:

  1. 不用多线程,这样就不会开启事务2,就不会造成锁等待

  1. 如果你的业务场景允许 事务1 先提交,那么先提交事务1即可,即将 BService的传播途径改为Propogation.REQUIRES_NEW,表示处于一个新事务中,methodB()结束,事务1就提交了,事务2就可以正常获取b表写锁:

  1. public class BService{
  2. @Transaction(propogation=Propogation.REQUIRES_NEW)
  3. public void methodB(){
  4. //删除B表中的数据
  5. 执行 delete from b where .....
  6. }
  7. }

如果有其他更好方式,请留言告知。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/1006756
推荐阅读
相关标签
  

闽ICP备14008679号