MySQL: 尝试获取锁时发现死锁
MySQL: Deadlock found when trying to get lock
此配置引发下一个异常:
- MySQL 5.7.10
- Spring 4.0.5
- 休眠 4.1.9
- Atomikos 3.8.0
- Spring ThreadPoolTaskExecutor 在 10 到 20 个线程之间
我需要 Atomikos 来处理两个数据库之间的事务。
例外情况是:
javax.persistence.PersistenceException: org.hibernate.exception.LockAcquisitionException: Deadlock found when trying to get lock; try restarting transaction
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:273)
at com.app.writer.jta.dao.CustomerDAOImpl.findCustomer(CustomerDAOImpl.java:35)
at com.app.writer.dao.impl.JTAPDFWriterDAO.saveBean(JTAPDFWriterDAO.java:64)
at com.app.writer.item.writer.PDFWriter.write(PDFWriter.java:17)
at org.springframework.batch.item.support.CompositeItemWriter.write(CompositeItemWriter.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy28.write(Unknown Source)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.writeItems(SimpleChunkProcessor.java:175)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.doWrite(SimpleChunkProcessor.java:151)
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.doWithRetry(FaultTolerantChunkProcessor.java:329)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:263)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:193)
at org.springframework.batch.core.step.item.BatchRetryTemplate.execute(BatchRetryTemplate.java:217)
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.write(FaultTolerantChunkProcessor.java:422)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:199)
at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:75)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at org.springframework.batch.core.step.tasklet.TaskletStep.doInChunkContext(TaskletStep.java:271)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:77)
at org.springframework.batch.repeat.support.TaskExecutorRepeatTemplate$ExecutingRunnable.run(TaskExecutorRepeatTemplate.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
我需要 PROCESS 权限来检查 SHOW ENGINE STATUS 以获取有关死锁的更多信息。
所以,我的问题是:
- 登录...CustomerDAOImpl.findCustomer是获取查询和锁table的钥匙。但是,死锁的产生是因为这个query/table还是另一个?
- 与 Atomikos 的交易是在两个数据库之间进行的。现在,我注意到所有相关的 table 都在 InnoDB 引擎中,除了一个在 MyISAM 中。这是正确的吗?会不会是这个异常的原因?
- 会不会是线程数导致的死锁?
当然,非常感谢任何检查和分析此异常的帮助。
提前致谢
[解决编辑]
Rohit 的建议很好。我接下来的行动是:
- 通过启用 SHOW ENGINE STATUS 并获取日志来获取有关死锁的更多信息。
- 我的交易在更新之前包含一些 SELECT。我将从事务中提取查询,因此它将只包含 INSERT 和 UPDATE 语句。
非常感谢。
1) 是,一个查询(SELECT/INSERT/UPDATE/DELETE)可以锁定数据库。
例如:SELECT 查询
SELECT * FROM CUSTOMER
//Making use of nolock as below will avoid deadlock but there are chances that you may not get the recent records just insert in your DB.
SELECT * FROM (nolock)CUSTOMER
2)当你使用两个或多个数据库时,事情会变得复杂,因为DB1将用于在其内部进行查询(与DB1相关) DB2 正在请求 DB1 触发一些查询(例如:通过触发器)。
例如: 在 DB1 中,您首先要保存一些要发送以供处理的记录(数据)到 java 应用。 java 应用程序正在使用 DB2 存储所有处理单元(客户数据等),一旦应用程序完成任务并将处理单元保存在 DB1 中,DB1 通过触发器通知 DB2 任务现已完成。
这会很好地工作,但是随着这里数据库服务器负载的增加,事情会变得复杂(多个线程执行 n 个事务),因此可能会发生死锁。
我想,您不会每时每刻都面临死锁问题。可能一天4-5次。这个问题在大型项目中很常见。另外,我猜你的事务在发生死锁时会回滚。
3) 下面列出了几种减少数据库中发生的死锁的方法。
3.1) DB发生死锁后可以通过处理org.hibernate.exception.LockAcquisitionException
.
执行某些任务
例如:一旦发生死锁,就向您的群组发送通知电子邮件。稍后,您必须手动重新设置事务。
3.2) 您可以做的其他事情是数据库服务器优化(调整),这将帮助您将死锁最小化到一定程度范围。
3.3) 通过控制数据库服务器的负载。
您的数据库服务器上可能有两种类型的负载,如下所示:
- Controlled Load(排队处理e.g.JMS,这里可以延迟请求处理一个单元)
- Uncontrolled Load(Users/Customer 正在访问您的数据库,这里您不能限制用户,所以不能在这种情况下任何东西)。
此配置引发下一个异常:
- MySQL 5.7.10
- Spring 4.0.5
- 休眠 4.1.9
- Atomikos 3.8.0
- Spring ThreadPoolTaskExecutor 在 10 到 20 个线程之间
我需要 Atomikos 来处理两个数据库之间的事务。
例外情况是:
javax.persistence.PersistenceException: org.hibernate.exception.LockAcquisitionException: Deadlock found when trying to get lock; try restarting transaction
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:273)
at com.app.writer.jta.dao.CustomerDAOImpl.findCustomer(CustomerDAOImpl.java:35)
at com.app.writer.dao.impl.JTAPDFWriterDAO.saveBean(JTAPDFWriterDAO.java:64)
at com.app.writer.item.writer.PDFWriter.write(PDFWriter.java:17)
at org.springframework.batch.item.support.CompositeItemWriter.write(CompositeItemWriter.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.doProceed(DelegatingIntroductionInterceptor.java:133)
at org.springframework.aop.support.DelegatingIntroductionInterceptor.invoke(DelegatingIntroductionInterceptor.java:121)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy28.write(Unknown Source)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.writeItems(SimpleChunkProcessor.java:175)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.doWrite(SimpleChunkProcessor.java:151)
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.doWithRetry(FaultTolerantChunkProcessor.java:329)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:263)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:193)
at org.springframework.batch.core.step.item.BatchRetryTemplate.execute(BatchRetryTemplate.java:217)
at org.springframework.batch.core.step.item.FaultTolerantChunkProcessor.write(FaultTolerantChunkProcessor.java:422)
at org.springframework.batch.core.step.item.SimpleChunkProcessor.process(SimpleChunkProcessor.java:199)
at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:75)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406)
at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330)
at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
at org.springframework.batch.core.step.tasklet.TaskletStep.doInChunkContext(TaskletStep.java:271)
at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:77)
at org.springframework.batch.repeat.support.TaskExecutorRepeatTemplate$ExecutingRunnable.run(TaskExecutorRepeatTemplate.java:262)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
我需要 PROCESS 权限来检查 SHOW ENGINE STATUS 以获取有关死锁的更多信息。
所以,我的问题是:
- 登录...CustomerDAOImpl.findCustomer是获取查询和锁table的钥匙。但是,死锁的产生是因为这个query/table还是另一个?
- 与 Atomikos 的交易是在两个数据库之间进行的。现在,我注意到所有相关的 table 都在 InnoDB 引擎中,除了一个在 MyISAM 中。这是正确的吗?会不会是这个异常的原因?
- 会不会是线程数导致的死锁?
当然,非常感谢任何检查和分析此异常的帮助。
提前致谢
[解决编辑] Rohit 的建议很好。我接下来的行动是:
- 通过启用 SHOW ENGINE STATUS 并获取日志来获取有关死锁的更多信息。
- 我的交易在更新之前包含一些 SELECT。我将从事务中提取查询,因此它将只包含 INSERT 和 UPDATE 语句。
非常感谢。
1) 是,一个查询(SELECT/INSERT/UPDATE/DELETE)可以锁定数据库。
例如:SELECT 查询
SELECT * FROM CUSTOMER
//Making use of nolock as below will avoid deadlock but there are chances that you may not get the recent records just insert in your DB.
SELECT * FROM (nolock)CUSTOMER
2)当你使用两个或多个数据库时,事情会变得复杂,因为DB1将用于在其内部进行查询(与DB1相关) DB2 正在请求 DB1 触发一些查询(例如:通过触发器)。
例如: 在 DB1 中,您首先要保存一些要发送以供处理的记录(数据)到 java 应用。 java 应用程序正在使用 DB2 存储所有处理单元(客户数据等),一旦应用程序完成任务并将处理单元保存在 DB1 中,DB1 通过触发器通知 DB2 任务现已完成。
这会很好地工作,但是随着这里数据库服务器负载的增加,事情会变得复杂(多个线程执行 n 个事务),因此可能会发生死锁。
我想,您不会每时每刻都面临死锁问题。可能一天4-5次。这个问题在大型项目中很常见。另外,我猜你的事务在发生死锁时会回滚。
3) 下面列出了几种减少数据库中发生的死锁的方法。
3.1) DB发生死锁后可以通过处理org.hibernate.exception.LockAcquisitionException
.
执行某些任务
例如:一旦发生死锁,就向您的群组发送通知电子邮件。稍后,您必须手动重新设置事务。
3.2) 您可以做的其他事情是数据库服务器优化(调整),这将帮助您将死锁最小化到一定程度范围。
3.3) 通过控制数据库服务器的负载。 您的数据库服务器上可能有两种类型的负载,如下所示:
- Controlled Load(排队处理e.g.JMS,这里可以延迟请求处理一个单元)
- Uncontrolled Load(Users/Customer 正在访问您的数据库,这里您不能限制用户,所以不能在这种情况下任何东西)。