如何在 mysql 中的一行上模拟死锁?

How to simulate a deadlock on a row in mysql?

要在 mysql 中模拟 lock 我可以使用以下内容获取该行:

BEGIN;
SELECT * FROM table WHERE id=1 FOR UPDATE;

现在,如果我尝试更新该行(从另一个连接),它将在 innodb_lock_wait_timeout 秒(默认值:50)后引发以下错误:

(1205, 'Lock wait timeout exceeded; try restarting transaction')

那么我将如何模拟死锁,所以我得到一个如下所示的错误:

Deadlock found when trying to get lock; try restarting transaction”

当我尝试查询或更新行时?


更新:即使在尝试模拟 the mysql deadlock example 时,我也会收到 Lock wait timeout exceeded; try restarting transaction 而不是 deadlock 消息。

是否启用了死锁检测?

您可以在这里阅读更多内容:https://dev.mysql.com/doc/refman/8.0/en/innodb-deadlock-detection.html

A mechanism that automatically detects when a deadlock occurs, and automatically rolls back one of the transactions involved (the victim). Deadlock detection can be disabled using the innodb_deadlock_detect configuration option.

在另一个事务中锁定另一个 table,然后尝试访问其他事务 table。 例如:

在交易中A锁定table1

在交易中B锁定table2

在事务A中更新table2

在事务B中更新table1

此外,您可以将超时增加到 5 分钟,这样在您创建死锁时它就不会超时。

更新: 一个例子

会话中 A:

START TRANSACTION;
UPDATE tbl1 SET b=1 WHERE id=1;

在会话中 B:

START TRANSACTION;
UPDATE tbl2 SET b=1 WHERE id=1;

然后

会话中 A:

UPDATE tbl2 SET b=1 WHERE id=1;

在会话中 B:

UPDATE tbl1 SET b=1 WHERE id=1;

首先,参考您上次的编辑,example in the manual 应该有效。如果没有,则可能存在根本性问题,或者您遗漏了一些细节,所以我会从这里开始,确保您能正常使用它。

死锁示例有 3 个步骤,我怀疑您可能错过了最后一个步骤:

  1. T1: select

  2. T2:delete。 T2 现在必须等待 T1。等待意味着,MySQL 目前仍然可以看到 T1 和 T2 都可以成功完成的可能方式!例如,T1 现在就可以提交。没有人知道,所以 T2 等待发生的事情。如果您在此步骤中等待的时间过长,您将超时(我怀疑发生了这种情况)。

  3. T1:delete。这将导致 T2 中的死锁。您需要这最后一步来创建 non-resolvable 冲突。

你应该先尝试那个例子,而且要小心,因为细节决定成败。在你自己的例子中引出一个细节:

您正在使用 SELECT ... FOR UPDATEFOR UPDATE 实际上是一种减少死锁数量的方法(这与你想要的相反),代价是锁定更严格。例如。你有更多的情况 MySQL 只是为了安全而等待,而不是继续并希望它最终会成功(或不会,因此陷入僵局)。请注意,出于这个原因,手册中的示例使用 LOCK IN SHARE MODE

所以要修改和扩展你自己的例子来获得死锁,你可以这样做

 T1: START TRANSACTION;
     SELECT * FROM table WHERE id=1 LOCK IN SHARE MODE;

 T2: START TRANSACTION;
     UPDATE table SET id=2 WHERE id=1 
      -- wait

 T1: UPDATE table SET id=2 WHERE id=1 
     -- deadlock in T2 

为了完整性(并排除潜在的误解):该行必须存在,如果您的 table 例如空,你不会陷入僵局。

如果您改用 FOR UPDATE,则不会出现死锁,但 T2 会一直等到您 commit/rollback T1。它与锁定的工作方式有关,但如果您将 select 添加到 T2:

,您也许可以了解这一点
 T1: START TRANSACTION;
     SELECT * FROM table WHERE id=1 LOCK IN SHARE MODE;

 T2: START TRANSACTION;
     SELECT * FROM table WHERE id=1 LOCK IN SHARE MODE;
     -- fine in shared mode. Waits here if you use `for update`!

 T1: UPDATE table SET id=2 WHERE id=1 
     -- wait

 T2: UPDATE table SET id=2 WHERE id=1 
     -- deadlock 

如果将 LOCK IN SHARE MODE 替换为 FOR UPDATE,T2 将等待 at/before 和 select,直到 T1 提交,不会出现死锁。