文档首页
MySQL 8.4 参考手册
相关文档 下载本手册
PDF (US Ltr) - 39.9Mb
PDF (A4) - 40.0Mb
手册页 (TGZ) - 258.5Kb
手册页 (Zip) - 365.5Kb
信息 (Gzip) - 4.0Mb
信息 (Zip) - 4.0Mb


MySQL 8.4 参考手册  /  ...  /  如何最小化和处理死锁

17.7.5.3 如何最小化和处理死锁

本节以第 17.7.5.2 节“死锁检测”中关于死锁的概念信息为基础。它解释了如何组织数据库操作以最大程度地减少死锁以及应用程序中所需的后续错误处理。

死锁是事务数据库中的一个经典问题,但除非它们频繁到您根本无法运行某些事务,否则它们并不危险。通常,您必须编写应用程序,以便在由于死锁而回滚事务时,它们始终准备好重新发出事务。

InnoDB使用自动行级锁定。即使在仅插入或删除单行的交易中,您也可能会遇到死锁。这是因为这些操作实际上并不是原子的;它们会自动锁定插入或删除的行的索引记录(可能有多个)。

您可以使用以下技术来应对死锁并降低其发生的可能性

  • 随时发出SHOW ENGINE INNODB STATUS以确定最近一次死锁的原因。这可以帮助您调整应用程序以避免死锁。

  • 如果频繁的死锁警告引起关注,请通过启用innodb_print_all_deadlocks变量来收集更广泛的调试信息。每个死锁(而不仅仅是最新一次)的信息都会记录在 MySQL 错误日志中。完成调试后禁用此选项。

  • 如果事务因死锁而失败,请始终准备好重新发出事务。死锁并不危险。再试一次即可。

  • 保持事务小而持续时间短,以减少发生冲突的可能性。

  • 在一组相关更改后立即提交事务,以减少发生冲突的可能性。尤其是,不要将交互式mysql会话长时间打开,而事务未提交。

  • 如果您使用锁定读取SELECT ... FOR UPDATESELECT ... FOR SHARE),请尝试使用较低的事务隔离级别,例如READ COMMITTED

  • 在事务中修改多个表或同一表中的不同行集时,请每次都以一致的顺序执行这些操作。然后,事务将形成定义明确的队列,并且不会发生死锁。例如,将数据库操作组织到应用程序中的函数中,或调用存储过程,而不是在不同位置编码多个类似的INSERTUPDATEDELETE语句序列。

  • 向表中添加精心选择的索引,以便查询扫描更少的索引记录并设置更少的锁。使用EXPLAIN SELECT确定 MySQL 服务器认为最适合您查询的索引。

  • 减少锁定。如果您可以允许SELECT从旧快照返回数据,请不要向其添加FOR UPDATEFOR SHARE子句。在这里使用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 表级锁时,必须使用超时方法来解决死锁。