文档版本:8.0
来源:How to Minimize and Handle Deadlocks
上一篇:快照读与加锁读html
本篇介绍如何减小死锁的发生,以及出现死锁时如何处理。
死锁指不一样的事务因彼此持有对方等待的锁而不能继续执行的情形。因双方都在等待资源释放,任意一方都不会释放已有的锁。mysql
死锁是事务形数据库中的经典问题,但只要死锁的发生不会频繁到彻底不能执行某个事务,那么就不算危险。一般,当事务因死锁而回滚时,你须要让你的应用随时作好从新发送事务的准备。算法
InnoDB引擎默认使用行锁。即便在事务中插入或删除单行数据,也能触发死锁。由于这些操做并非真正的“原子的”,在插入或删除时它们会给行的索引值(可能有多个)上锁。sql
在前面的 InnoDB中的锁 中解释过:在默认级别
REPEATABLE READ
下,InnoDB在搜索和扫描索引时使用邻键锁,用于避免幻行。此时InnoDB会无视Where条件的过滤,给每一个扫描到的索引值及其间隙上锁。此策略适用于加锁读、INSERT、DELETE,但有一个特殊场景只会上记录锁不会上间隙锁:WHERE条件中涵盖了惟一索引。数据库
经过如下技巧,你能够处理好死锁,并减小其发生的几率:并发
随时使用SHOW ENGINE INNODB STATUS
找出最近发生死锁的缘由,以便调整应用规避死锁。函数
若是频繁的死锁警告惹人注目,开启innodb_print_all_deadlocks
选项以收集额外的调试信息。在MySQL的错误日志中会记录每次死锁的信息,而不单单记录最后一次。完成调试后关闭这个选项。设计
随时准备好重启因死锁而失败的事务。死锁并不危险,重试就行了。调试
保持事务的短小精悍,以下降冲突的可能性。日志
作出一系列关联变动后当即提交事务,以下降冲突的可能性。特别是不要让有关联的MySQL会话长时间挂起未提交的事务。
若是使用加锁读(SELECT ... FOR UPDATE
或 SELECT ... FOR SHARE
),尝试使用更低的隔离级别,如READ COMMITTED
。
在同一事务内修改多张表,或一张表内的不一样行时,每次以相同的顺序执行操做。以便让事务造成清晰的锁操做队列而规避死锁。例如,将数据库操做编排为应用内的函数,或调用SQL序列(即MySQL函数或存储过程),避免将相似的INSERT,UPDATE和DELETE操做分散在代码各处。
精心设计表索引。让查询扫描更少的行数,也就意味着更少的锁。使用EXPLAIN SELECT
查看MySQL认为查询最适合使用的索引。
减小锁操做。若是能容忍查询返回老快照中的数据,就不要加FOR UPDATE
或FOR SHARE
子句。READ COMMITTED
级别适合这种场景,由于每次快照读都会读取本身的最新快照。
SET autocommit=0; LOCK TABLES t1 WRITE, t2 READ, ...; ... 在表t1和t2上操做 ... COMMIT; UNLOCK TABLES;
表级锁防止表中的并发更改,经过牺牲响应度避免了死锁。