记录一次数据库死锁;Deadlock found when trying to get lock;DeadlockLoserDataAccessException

线上异常

org.springframework.dao.DeadlockLoserDataAccessException: 
### Error updating database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
### The error may involve com.ncars.jxc.stock.mapper.OrderDeliverInfoMapper.updateByPrimaryKeySelective-Inline
### The error occurred while setting parameters
### SQL: update `stock_order_deliver_info`          SET `order_state`=?,                                                                                                                                                                                                                                                                                                                                               `update_user_id`=?,                                            `update_user_name`=?,                                                        `update_time`=?,                                           `deleted`=?          where id = ?
### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
; ]; Deadlock found when trying to get lock; try restarting transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
	at org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator.doTranslate(SQLErrorCodeSQLExceptionTranslator.java:266)
	at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
	at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:75)
	at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:447)
	at com.sun.proxy.$Proxy94.update(Unknown Source)
	at org.mybatis.spring.SqlSessionTemplate.update(SqlSessionTemplate.java:295)
	at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:59)
	at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:53)
	at com.sun.proxy.$Proxy106.updateByPrimaryKeySelective(Unknown Source)
	at com.ncars.jxc.common.service.BaseService.updateByPrimaryKeySelective(BaseService.java:38)
	at com.ncars.jxc.stock.rest.impl.OrderDeliverInfoRestServiceImpl.updateStatus(OrderDeliverInfoRestServiceImpl.java:1336)
	at com.ncars.jxc.stock.rest.impl.OrderDeliverInfoRestServiceImpl.updateDeliverInfoState(OrderDeliverInfoRestServiceImpl.java:1284)
	at com.ncars.jxc.stock.rest.impl.OrderDeliverInfoRestServiceImpl$$FastClassBySpringCGLIB$$b9cb28e9.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:747)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)

14.7.5 InnoDB中的死锁

死锁是指由于每个事务都持有对方需要的锁而无法进行其他事务的情况。因为这两个事务都在等待资源变得可用,所以都不会释放它持有的锁。
当事务锁定多个表中的行时(通过诸如UPDATE或SELECT … FOR UPDATE之类的语句),但顺序相反,则会发生死锁。当此类语句锁定索引记录和间隙的范围时,由于时序问题,每个事务都获得了一些锁而没有获得其他锁,也会发生死锁。有关死锁的示例,请参见第14.7.5.1节“ InnoDB死锁的示例”。
为了减少发生死锁的可能性,请使用事务而不是LOCK TABLES语句。保持用于插入或更新数据的事务足够小,以使其长时间不保持打开状态;当不同的事务更新多个表或大范围的行时,在每个事务中使用相同的操作顺序(例如SELECT … FOR UPDATE);在SELECT … FOR UPDATE和UPDATE … WHERE语句中使用的列上创建索引。死锁的可能性不受隔离级别的影响,因为隔离级别更改了读取操作的行为,而死锁则是由于写入操作而发生的。有关避免和从死锁条件中恢复的更多信息,请参见第14.7.5.3节“如何最小化和处理死锁”。
如果确实发生了死锁,InnoDB会检测到该情况并回滚其中一个事务(受害方)。因此,即使您的应用程序逻辑正确,您仍必须处理必须重试事务的情况。要查看InnoDB用户事务中的最后一个死锁,请使用SHOW ENGINE INNODB STATUS命令。如果频繁的死锁凸显了事务结构或应用程序错误处理方面的问题,请在启用innodb_print_all_deadlocks设置的情况下运行,以将有关所有死锁的信息打印到mysqld错误日志中。有关如何自动检测和处理死锁的更多信息,请参见第14.7.5.2节“死锁检测和回滚”。

链接: https://dev.mysql.com/doc/refman/5.6/en/innodb-deadlocks.html.

14.7.5.1 InnoDB死锁的示例

排它锁(X锁)和共享锁(S锁).
所谓X锁,是事务T对数据A加上X锁时,只允许事务T读取和修改数据A
所谓S锁,是事务T对数据A加上S锁时,其他事务只能再对数据A加S锁,而不能加X锁,直到T释放A上的S锁
若事务T对数据对象A加了S锁,则T就可以对A进行读取,但不能进行更新(S锁因此又称为读锁),在T释放A上的S锁以前,其他事务可以再对A加S锁,但不能加X锁,从而可以读取A,但不能更新A.

以下示例说明了锁定请求将导致死锁时如何发生错误。该示例涉及两个客户端A和B。
首先,客户端A创建一个包含一行的表,然后开始事务。在事务中,A通过在共享模式下选择该行来获得该行的S锁:

mysql> CREATE TABLE t (i INT) ENGINE = InnoDB;
Query OK, 0 rows affected (1.07 sec)
mysql> INSERT INTO t (i) VALUES(1);
Query OK, 1 row affected (0.09 sec)
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE;
+------+
| i    |
+------+
|    1 |
+------+

接下来,客户端B开始事务并尝试从表中删除该行:

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> DELETE FROM t WHERE i = 1;

删除操作需要X锁。无法授予该锁,因为它与客户端A持有的S锁不兼容,因此该请求进入针对行和客户端B块的锁请求队列中。
最后,客户端A还尝试从表中删除该行:

mysql> DELETE FROM t WHERE i = 1;
ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction

由于客户端A需要X锁才能删除行,因此在此处发生死锁。但是,不能授予该锁定请求,因为客户端B已经具有X锁定请求,并且正在等待客户端A释放其S锁定。由于B事先要求X锁,因此A持有的S锁也不能升级为X锁。结果,InnoDB为其中一个客户端生成一个错误并释放其锁。客户端返回此错误:

ERROR 1213 (40001): Deadlock found when trying to get lock;
try restarting transaction

届时,可以授予对另一个客户端的锁定请求,并从表中删除该行。

链接: https://dev.mysql.com/doc/refman/5.6/en/innodb-deadlock-example.html.

14.7.5.3 如何最小化和处理死锁

本节以第14.7.5.2节“死锁检测和回滚”中有关死锁的概念性信息为基础。它说明了如何组织数据库操作以最大程度地减少死锁和应用程序中所需的后续错误处理。
死锁是事务数据库中的经典问题,但是它们没有危险,除非它们如此频繁以至于您根本无法运行某些事务。通常,您必须编写应用程序,以便在由于死锁而使事务回滚时,它们始终准备重新发出事务。
InnoDB使用自动行级锁定。即使在仅插入或删除单行的事务中,也可能会遇到死锁。这是因为这些操作并不是真正的“原子”操作;它们会自动对插入或删除的行的(可能是多个)索引记录设置锁定。
您可以使用以下技术来处理死锁并减少发生死锁的可能性:
在任何时候,发出SHOW ENGINE INNODB STATUS命令来确定最新死锁的原因。这可以帮助您调整应用程序以避免死锁。
如果频繁出现死锁警告引起关注,请通过启用innodb_print_all_deadlocks配置选项来收集更多的调试信息。有关每个死锁的信息,不仅是最新的死锁,还记录在MySQL错误日志中。完成调试后,请禁用此选项。
如果由于死锁而失败,请始终准备重新发出事务。死锁并不危险。请再试一次。
保持交易小巧且持续时间短,以使交易不易发生冲突。
进行一系列相关更改后立即提交事务,以减少冲突的发生。特别是,不要长时间未提交事务而使交互式mysql会话保持打开状态。
如果使用锁定读取(SELECT … FOR UPDATE或SELECT … LOCK IN SHARE MODE),请尝试使用较低的隔离级别,例如READ COMMITTED。
修改事务中的多个表或同一表中的不同行集时,每次都要以一致的顺序执行这些操作。然后,事务形成定义明确的队列,并且不会死锁。例如,将数据库操作组织到应用程序中的函数中,或调用存储的例程,而不是在不同位置编码多个类似的INSERT,UPDATE和DELETE语句序列。
将选择好的索引添加到表中。然后,您的查询需要扫描更少的索引记录,并因此设置更少的锁。使用EXPLAIN SELECT来确定MySQL服务器认为哪个索引最适合您的查询。
使用更少的锁定。如果您有能力允许SELECT从旧快照返回数据,请不要在其中添加FOR UPDATE或LOCK IN SHARE MODE子句。在这里使用READ COMMITTED隔离级别是件好事,因为同一事务中的每个一致读取都从其自己的新快照读取。
如果没有其他帮助,请使用表级锁序列化事务。将LOCK TABLES与事务表(例如InnoDB表)一起使用的正确方法是,先以SET autocommit = 0(不是START TRANSACTION)后跟LOCK TABLES开始事务,并且在明确提交事务之前不调用UNLOCK TABLES。例如,如果您需要写入表t1并从表t2中读取,则可以执行以下操作:

SET autocommit=0;
LOCK TABLES t1 WRITE, t2 READ, ...;... do something with tables t1 and t2 here ...
COMMIT;
UNLOCK TABLES;

表级锁可防止对表的并发更新,从而避免死锁,但代价是对繁忙系统的响应速度较慢。
序列化事务的另一种方法是创建一个仅包含一行的辅助“信号量”表。在访问其他表之前,让每个事务更新该行。这样,所有事务都以串行方式发生。注意,在这种情况下,InnoDB即时死锁检测算法也适用,因为序列化锁是行级锁。对于MySQL表级锁,必须使用超时方法来解决死锁。

链接: https://dev.mysql.com/doc/refman/5.6/en/innodb-deadlocks-handling.html.

14.7.5.2 死锁检测和回滚

InnoDB自动检测事务死锁并回滚一个或多个事务以打破死锁。 InnoDB会尝试选择小的事务以进行回滚,其中事务的大小由插入,更新或删除的行数决定。
如果innodb_table_locks = 1(默认值)且autocommit = 0,则InnoDB知道表锁,并且它上面的MySQL层知道行级锁。否则,在涉及由MySQL LOCK TABLES语句设置的表锁或由InnoDB以外的存储引擎设置的锁的情况下,InnoDB无法检测到死锁。通过设置innodb_lock_wait_timeout系统变量的值来解决这些情况。
当InnoDB对事务执行完全回滚时,将释放该事务设置的所有锁。但是,如果由于错误而仅回滚单个SQL语句,则可以保留该语句设置的某些锁。之所以会发生这种情况,是因为InnoDB以某种格式存储行锁,使得以后无法知道哪个语句设置了哪个锁。
如果SELECT在事务中调用存储的函数,并且函数内的语句失败,则该语句将回滚。此外,如果此后执行ROLLBACK,则整个事务都会回滚。
如果InnoDB Monitor输出的“最新检测到的DEADLOCK”部分包含一条消息,指出“锁表等待太深或长时间搜索-对于图形,我们将回滚后续事务”,这表明等待等待的事务数list已达到限制200。超过200个事务的等待列表将被视为死锁,并且尝试检查等待列表的事务将回退。如果锁定线程必须查看等待列表上的事务拥有的1,000,000个以上的锁,也可能发生相同的错误。
有关组织数据库操作以避免死锁的技术,请参见第14.7.5节“ InnoDB中的死锁”。

链接: https://dev.mysql.com/doc/refman/5.6/en/innodb-deadlock-detection.html.

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当尝试获取锁时发现死锁。这个问题可能是由于并发事务之间的争用锁资源引起的。为了解决这个问题,可以尝试以下几种方法: 1. 序列化事务方法:创建一个仅包含一行的辅助“信号量”表,并让每个事务在访问其他表之前更新该行。这样可以确保所有事务以串行方式发生,避免了死锁的发生。请注意,这种方法适用于InnoDB引擎的行级锁,对于MySQL表级锁,则需要使用超时方法来解决死锁。 [1] 2. 降低隔离级别:如果您使用锁定读取(SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE),可以尝试使用较低的隔离级别,例如READ COMMITTED。这样可以减少锁的数量,降低死锁的概率。 [2] 3. 减少锁的使用:如果您可以承受读取旧快照的数据,可以避免使用FOR UPDATE或LOCK IN SHARE MODE来允许SELECT语句返回旧快照的数据。在READ COMMITTED隔离级别下,每个一致性读取都会从自己的新快照中读取,这样可以减少锁的使用。 [3] 以上是解决"Deadlock found when trying to get lock"问题的几种方法。根据具体情况,您可以选择适合您应用程序的方法来避免死锁的发生。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [难得一见的数据库事务异常 Deadlock found when trying to get lock解决办法dao....](https://blog.csdn.net/u011488009/article/details/111173576)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值