出问题了
这里是一个重试发送的一段逻辑,这个方法上面开启了事务,在tryRetry时,实际上是修改某条记录的状态,我们认为它是id=6好了,
然后debug后,现在的代码执行到了第二条红线处(如下图),然后我放开断点,让其往下执行,然后发现程序一直“卡住”,不能跳到下一个断点int i = 1/0处,很奇怪,不就是save方法嘛,这里save方法实际上也是修改,然后我们查看这条事务的执行状态如下:
发现有条事务,一直在等待,LOCK_WAIT状态,这里等待多久,跟我前面说的事务隔离那篇博客 https://blog.csdn.net/weixin_43939086/article/details/89167285中的innodb_lock_wait_timeout设置时长有关,设置多久,就会等待多久,等待这个时长后,debug到跳到断点 int i = 1/0处,如下图,
这时候,发现没有,那个save代码(update语句)已经放开了,尝试获取锁“innodb_lock_wait_timeout”时长之后,终于放开了,这时候也看到下面的事务列表中,也刚好少了那条,然后我们再放开断点,结果控制台,也抛出了错误:
2019-04-08 18:32:23.557 WARN 1652 — [ scheduling-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1205, SQLState: 40001
2019-04-08 18:32:23.557 ERROR 1652 — [ scheduling-1] o.h.engine.jdbc.spi.SqlExceptionHelper : Lock wait timeout exceeded; try restarting transaction
2019-04-08 18:32:23.572 ERROR 1652 — [ scheduling-1] o.h.i.ExceptionMapperStandardImpl : HHH000346: Error during managed flush [could not execute statement]
实际还有但是这里被1/0抛出的异常掩盖了。所以还抛出了:java.lang.ArithmeticException: / by zero
我们去掉int i = 1/0,这段代码重启重新调试试下
结果就抛出了死锁引起等待超时的一些异常:
org.springframework.dao.PessimisticLockingFailureException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.PessimisticLockException: could not execute statement
Caused by: org.hibernate.PessimisticLockException: could not execute statement
Caused by: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
ps:为什么会出现死锁呢,同一个事务里面不是对同一条记录,是可重入的,直接读的嘛,为什么会出现死锁呢,难道真的有两个会话事务嘛,那save代码,是不是又开启了一个事务呢,于是我想到ibernate有session的概念呢,这不正对应我们前面将的设置客户端事务(也来自 https://blog.csdn.net/weixin_43939086/article/details/89167285种的介绍)时用的开启事务语句嘛,set session transaction isolation level read committed ;这里面不是也有一个session嘛。
因此猜想,是不是前面tryRetry代码已经开启了一个客户端事务,现在用save实际上就是用hibernate的 session又开启了一个事务,在外事务还没有提交该记录时,此时,这个save事务,根本就获取不到记录的锁执行了,所有就出现死锁了,只有等到数据库的锁失效才能获取了,因此等待一段时间是跳过了,但是却抛出了异常。
至此,已经解释了此次“暂时死锁”造成的原因,原来就是hibernate中,如果对象已经在session中持久化了,再save的又会开启一个事务,于是就死锁了。