在Mysql文档中:"https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks-handling.html“
它提到:“如果使用锁读(选择.用于更新或选择.锁定共享模式),尝试使用较低的隔离级别,如ReadCommit.”
有人能告诉我为什么我不能用“可重复阅读”吗?以身作则会很好。
干杯
发布于 2017-09-15 04:21:11
如果使用读提交,InnoDB将避免使用某些类型的锁。这可以帮助您避免死锁。
我设计了一个完整的演示文稿:用棍子图解释InnoDB锁定。
但实际上你永远也无法避免100%的死锁。它们不是一个错误,它们是并发系统的自然组成部分。您可以减少死锁发生的频率,但您最好还是习惯于获取一些死锁。设计代码以捕获异常,并在出现死锁时重试数据库操作。
发布于 2021-11-24 21:25:21
原因是,如果表中不存在记录,那么当SELECT ... FOR UPDATE隔离模式处于活动状态时,它看起来就像在共享模式下(或类似的东西--在任何地方都没有文档记录)获取索引记录的下键锁。
让我们尝试一个在t隔离模式下为空的简单表REPEATABLE READ的示例。
t1> SELECT * FROM t WHERE id = 1 FOR UPDATE;
no rows found, next-key lock acquired in shared mode
t2> SELECT * FROM t WHERE id = 1 FOR UPDATE;
no rows found, next-key lock acquired in shared mode
t1> INSERT INTO t (id) VALUES (1);
transaction t1 is blocked by t2
t2> INSERT INTO t (id) VALUES (1);
transaction t2 is blocked by t1 - deadlock即使第二个SELECT和INSERT将使用id=2,也会发生死锁,因为它也落入了由SELECT ... FOR UPDATE锁定的在t1中执行的相同空白中。如果表是空的,这个间隙就是无穷大。如果表不为空,则插入不同记录的死锁概率较小,但仍然很大(这取决于表中有多少间隙,以及插入到表尾的频率--最大的差距)。
之所以会发生这种情况,是因为当记录不存在时,来自t1和t2 的不会互相阻塞。对于现有的记录,它在t1中获取记录上的X(独占)锁,因此t2将被阻塞,直到t1被提交或回滚。但是,如果记录不存在--它获取空白上的S(共享)下一个键锁(我不确定它是否真的是S锁(在任何地方都没有记录),但是MySQL如何允许在相同的间隙上同时获得2个锁?)。这是死锁的主要原因-- t1和t2都试图获取间隙上的IX (插入意图)锁,然后在插入的记录上获得X锁,但都因为SELECT ... FOR UPDATE获得的锁而彼此等待。
当使用READ COMMITED事务隔离级别时,不存在此问题。如果没有找到记录,并且使用了SELECT ... FOR UPDATE隔离级别,则READ COMMITED不会持有任何锁。所以第一个INSERT将会成功。第二个INSERT将被第一个INSERT获得的独占锁所阻塞,而在t1被提交之后,第二个INSERT只会抛出Duplicate entry '1' for key 'PRIMARY'。
你现在可以认为现在的情况并不是更好的僵局。只是又一个错误。但是现在假设第二个INSERT试图用id=2插入一条记录。在这种情况下,它不会被t1阻止,这两个事务都会成功。对于某些应用程序来说,这是一个大差异。
https://stackoverflow.com/questions/46230555
复制相似问题