Day89 【概念解析】
目录 ▼
整理定义
本章为死锁实践,主要是对于 Day88 的死锁检测中进行一些拓展。
解释了如何组织数据库操作以最大限度地减少死锁以及应用程序中所需的后续错误处理。
死锁是事务数据库中的一个典型问题,但它们并不危险,除非它们太频繁以至于您根本无法运行某些事务。 通常,您必须编写应用程序,以便它们始终准备好在事务因死锁而回滚时重新发出事务。
InnoDB 使用自动行级锁定。 即使事务只插入或删除一行,也可能会出现死锁。 这是因为这些操作并不是真正的“原子”; 它们自动在插入或删除的行的(可能是多个)索引记录上设置锁。
-
随时使用 SHOW ENGINE INNODB STATUS 来确定最近一次死锁的原因。这可以帮助你调整应用程序以避免死锁。
-
如果频繁的死锁警告引起关注,通过启用 innodb_print_all_deadlocks 变量来收集更多的调试信息。MySQL错误日志中会记录每一次死锁的信息,而不仅仅是最新的一次。在完成调试后禁用此选项。
-
始终准备重新发起因死锁而失败的事务。死锁并不危险。只需再试一次。
-
保持事务小而持续时间短,以使它们不太容易发生冲突。
-
在进行一系列相关更改后立即提交事务,以使它们不太容易发生冲突。特别是,不要在交互式mysql会话中长时间保持未提交的事务。
-
如果你使用锁定读(SELECT … FOR UPDATE 或 SELECT … FOR SHARE),尝试使用较低的隔离级别,如 READ COMMITTED。
-
在事务中修改多个表,或在同一表中修改不同的行集时,每次都按照一致的顺序进行这些操作。这样,事务形成良好定义的队列,不会发生死锁。例如,在应用程序中将数据库操作组织成函数,或者调用存储过程,而不是在不同的地方编写多个类似的 INSERT、UPDATE 和 DELETE 语句序列。
-
为你的表添加合适的索引,这样你的查询就会扫描更少的索引记录并设置更少的锁。使用 EXPLAIN SELECT 来确定 MySQL 服务器认为哪些索引最适合你的查询。
-
使用更少的锁定。如果你可以允许 SELECT 返回旧快照中的数据,那么不要在它上面添加 FOR UPDATE 或 FOR SHARE 子句。在这里使用 READ COMMITTED 隔离级别是好的,因为同一事务中的每个一致读取都来自它自己的新鲜快照。
-
如果其他方法都无效,可以通过表级锁来序列化你的事务。与事务性表(如InnoDB表)一起使用LOCK TABLES的正确方法是先用SET autocommit = 0(不是START TRANSACTION)开始一个事务,然后是LOCK TABLES,并且在你明确提交事务之前不要调用UNLOCK TABLES。例如,如果你需要对表t1进行写操作并从表t2读取,你可以这样做:
-
另一种序列化事务的方法是创建一个只包含单行的辅助“信号量”表。在访问其他表之前,让每个事务更新那一行。这样,所有事务都会以串行方式发生。请注意,InnoDB即时死锁检测算法在这种情况下也有效,因为序列化锁是一个行级锁。对于MySQL表级锁,必须使用超时方法来解决死锁。
复述展开
How to Minimize and Handle Deadlocks
理解体会
上面的内容偏向实战一些,可以多多考虑下。