死锁是一种不同事务无法继续进行的情况,核心原因是每个事务都持有某个另一个事务所需的锁。因为双方事务都在等待资源变为可用状态,所以它们都不会释放它所持有的锁。
当事务锁定多个表中的行(通过 UPDATE
或 SELECT ... FOR UPDATE
等语句),但彼此加锁的顺序相反时,可能会发生死锁。当这样的语句锁定索引记录和间隙的范围时,也会发生死锁,因为每个事务都会由于时序问题获得部分锁,而获取不了其他范围的锁。
为了减少死锁的可能性,可以采取以下措施:
-
应使用事务而不是锁表语句。
-
将插入或更新数据的事务保持在足够小的范围内,使其不会长时间处于打开状态。
-
当不同的事务更新多个表或大范围的行时,在每个事务中使用相同的操作顺序(例如
SELECT ... FOR UPDATE
) -
在使用
SELECT ... FOR UPDATE
和UPDATE ... WHERE
的列上创建索引
发生死锁的可能性不受隔离级别的影响,因为隔离级别会更改读取操作的行为,而死锁是由于写入操作而发生的。
当启用死锁检测(默认开启)且死锁确实发生时,InnoDB 检测到该情况并回滚其中一个事务。如果使用 innodb_deadlock_detect
变量禁用死锁检测,innodb
将基于 innodb_lock_wait_timeout
配置在发生死锁时回滚事务。因此,即使应用程序逻辑正确,也必须处理必须重试事务的情况。
要查看 InnoDB 用户事务中的最后一个死锁,请使用 SHOW ENGINE INNODB STATUS
。
如果频繁的死锁,这说明事务结构或应用程序存在错误逻辑,请启用 innodb_print_all_deadlocks
将所有死锁的信息打印到 mysqld 错误日志中。