即使使用LockModeType.PESSIMISTIC_WRITE也会出现死锁

时间:2017-08-22 12:46:36

标签: java spring hibernate spring-data-jpa

当我在1秒内触发10个请求时,org.springframework.dao.CannotAcquireLockException 发生。

即使我用@Transactional(isolation = Isolation.SERIALIZABLE)标记了我的main方法(在里面调用存储库方法),我的存储库中的findBy方法也有@Lock(LockModeType.PESSIMISTIC_WRITE)注释。

在一个环境中一切正常,但在另一个环境中发生错误。任何想法错误发生的想法?两种环境下的代码都是相同的。

Stacktrace 1:

  

{"时间戳":" 2017-08-21T12:18:16.16 + 0000""状态" 500"错误&#34 ;:"内部服务器错误","例外":" org.springframework.orm.jpa.JpaSystemException","消息":   " com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:尝试获取锁定时发现死锁;尝试重启交易;嵌套异常是   javax.persistence.PersistenceException:com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException:尝试获取锁定时发现死锁;尝试重新启动   交易""路径":" /汽车/丰田"}

Stacktrace 2:

  

{"时间戳":" 2017-08-21T12:11:57.57 + 0000""状态" 500"错误&#34 ;:"内部服务器错误","例外":" org.springframework.dao.CannotAcquireLockException"," message":"无法执行声明; SQL [不适用];   嵌套异常是org.hibernate.exception.LockAcquisitionException:无法执行语句"," path":" / cars / toyota"}

1 个答案:

答案 0 :(得分:1)

当您尝试锁定行但该行已被另一个事务锁定时,会引发错误。所以超时发生了。

添加foreach (var employee in model.Employees) { if (employee.Selected == true) { var commissionemployeeposition = new CommissionEmployeePosition(); commissionemployeeposition.CommissionId = commission.Id; commissionemployeeposition.EmployeeId = employee.Id; if (model.SupervisorID == commissionemployeeposition.EmployeeId) commissionemployeeposition.EmployeePositionId = (int)Position.Supervisor; else commissionemployeeposition.EmployeePositionId = (int)Position.Member; //commission.CommissionEmployeePositions.Add(commissionemployeeposition); unitOfWork.CommissionEmployeePositionRepository.Insert(commissionemployeeposition); } } 会使情况变得更糟,因为可序列化级别意味着一个事务锁定整个表(现代oracle可以某种方式控制它,但对于MySQl,它实际上是一个等待锁定同一个表的事务队列)。因此,第10次交易等待所有之前的9次交易并失败。

实际上我不明白为什么你需要锁定public class CommissionEmployeePosition { public int CommissionId { get; set; } public int EmployeeId { get; set; } public int EmployeePositionId { get; set; } public Commission Commission { get; set; } public Employee Employee { get; set; } public EmployeePosition EmployeePosition { get; set; } } 方法。从逻辑上讲,该方法不应该修改表,因此根本不需要锁定。

如果您有并发修改,我建议更改逻辑以更快地进行一次调用,甚至引入乐观锁(基于版本)。 Hibernate允许添加@Transactional(isolation = Isolation.SERIALIZABLE)