MySQL的MDL(元数据锁)为什么会导致死锁?

huangapple go评论60阅读模式
英文:

Why MySQL MDL(meta data lock) led to a deadlock?

问题

我正在学习MySQL锁,并在MDL方面遇到一些问题。

MySQL版本是8.0。

有一个名为"account"的InnoDB表。

会话1:

begin; // 开始事务

select * from table account; // 获取MDL SHARED_READ 锁

会话2:

alter table account add columnA int; // 尝试获取MDL EXCLUSIVE 锁,等待会话1释放锁

然后我尝试在会话1中向"account"表插入记录,但MySQL告诉我发生了死锁,并自动回滚了会话1。

我尝试了一些不同的操作:

会话1:

begin; // 开始事务

insert into account values ('test'); // 获取MDL SHARED_WRITE 锁

会话2:

alter table account add columnA int; // 尝试获取MDL EXCLUSIVE 锁,等待会话1释放锁

然后我发现在会话1中进行插入和选择操作是可以的。

这真的让我感到困惑,为什么我首先使用select操作来获取MDL SHARED_READ锁会导致死锁?

英文:

I'm learning MySQL locks and found some problems with the MDL.

The MySQL verison is 8.0.

There is a table named account using InnoDB.

session 1:

begin; // start the transaction

select * from table account; // acquire the MDL SHARED_READ lock

session 2:

alter table account add columnA int; // try to acquire the MDL EXCLUSIVE lock, wait for session 1 to release lock

Then I try to insert a record to table account in session 1, but MySQL tells me there is a deadlock and rollback session 1 automatically.

And I try something different:

session 1:

begin; // start the transaction

insert into account values ('test'); // acquire the MDL SHARED_WRITE lock

session 2:

alter table account add columnA int; // try to acquire the MDL EXCLUSIVE lock, wait for session 1 to release lock

Then I found it is OK to do the insert and select operations in session 1.

It really confused me, why I first use the select operation to acquire MDL SHARED_READ lock led to a deadlock?

答案1

得分: 2

由于有不同类型的MDL共享锁,SHARED_READ和SHARED_WRITE。

会话1:

begin; // 开始事务

select * from table account; 
// 获取MDL SHARED_READ锁定

我们可以观察到这个MDL:

mysql> select object_name, lock_type, owner_thread_id from performance_schema.metadata_locks;
+----------------+--------------+-----------------+
| object_name    | lock_type    | owner_thread_id |
+----------------+--------------+-----------------+
| account        | SHARED_READ  |              64 |

会话2:

alter table account add columnA int; 
// 尝试获取MDL EXCLUSIVE锁,等待会话1释放锁定

mysql> select object_name, lock_type, owner_thread_id from performance_schema.metadata_locks;
+----------------+---------------------+-----------------+
| object_name    | lock_type           | owner_thread_id |
+----------------+---------------------+-----------------+
| account        | SHARED_READ         |              64 |
| NULL           | INTENTION_EXCLUSIVE |              65 |
| NULL           | INTENTION_EXCLUSIVE |              65 |
| NULL           | INTENTION_EXCLUSIVE |              65 |
| account        | SHARED_UPGRADABLE   |              65 |
| test/account   | INTENTION_EXCLUSIVE |              65 |
| #sql-255_17    | EXCLUSIVE           |              65 |
| account        | EXCLUSIVE           |              65 |

会话1:

要执行插入操作,会话1必须获取一种新类型的MDL锁,但在会话2排队等待MDL EXCLUSIVE锁定的情况下无法执行。

mysql> insert into account () values ();
// 尝试获取MDL SHARED_WRITE锁
// 等待会话2释放其排队锁定请求

因此,两个会话都在等待对方,都无法继续。这是死锁。

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

第二个实验:

会话1:

begin; // 开始事务

select * from table account for update; 
// 获取MDL SHARED_WRITE锁定

我们可以观察到这个MDL:

mysql> select object_name, lock_type, owner_thread_id from performance_schema.metadata_locks;
+----------------+--------------+-----------------+
| object_name    | lock_type    | owner_thread_id |
+----------------+--------------+-----------------+
| account        | SHARED_WRITE |              64 |

这是一种不同类型的MDL。它允许插入操作。

会话2:

alter table account add columnA int; 
// 尝试获取MDL EXCLUSIVE锁,等待会话1释放锁定

与前一个实验相同,DDL语句等待。

会话1:

mysql> insert into account () values ();
Query OK, 1 row affected (0.00 sec)

不需要额外的锁定。会话1已经具有执行插入操作所需的MDL SHARED_WRITE锁,因此可以继续。

英文:

Because there are different types of MDL shared locks, SHARED_READ and SHARED_WRITE.

session 1:

begin; // start the transaction

select * from table account; 
// acquire the MDL SHARED_READ lock

We can observe this MDL:

mysql> select object_name, lock_type, owner_thread_id from performance_schema.metadata_locks;
+----------------+--------------+-----------------+
| object_name    | lock_type    | owner_thread_id |
+----------------+--------------+-----------------+
| account        | SHARED_READ  |              64 |

session 2:

alter table account add columnA int; 
// try to acquire the MDL EXCLUSIVE lock, wait for session 1 to release lock

mysql> select object_name, lock_type, owner_thread_id from performance_schema.metadata_locks;
+----------------+---------------------+-----------------+
| object_name    | lock_type           | owner_thread_id |
+----------------+---------------------+-----------------+
| account        | SHARED_READ         |              64 |
| NULL           | INTENTION_EXCLUSIVE |              65 |
| NULL           | INTENTION_EXCLUSIVE |              65 |
| NULL           | INTENTION_EXCLUSIVE |              65 |
| account        | SHARED_UPGRADABLE   |              65 |
| test/account   | INTENTION_EXCLUSIVE |              65 |
| #sql-255_17    | EXCLUSIVE           |              65 |
| account        | EXCLUSIVE           |              65 |

session 1:

To do an insert, session 1 would have to acquire a new type of MDL lock, but it can't do that while session 2 is queued waiting for a MDL EXCLUSIVE lock.

mysql> insert into account () values ();
// try to acquire a MDL SHARED_WRITE lock
// wait for session 2 to release its queued lock request

Thus both sessions are waiting for the other, and neither can proceed. This is a deadlock.

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

Second experiment:

session 1:

begin; // start the transaction

select * from table account for update; 
// acquire the MDL SHARED_WRITE lock

We can observe this MDL:

mysql> select object_name, lock_type, owner_thread_id from performance_schema.metadata_locks;
+----------------+--------------+-----------------+
| object_name    | lock_type    | owner_thread_id |
+----------------+--------------+-----------------+
| account        | SHARED_WRITE |              64 |

This is a different type of MDL. It allows inserts.

session 2:

alter table account add columnA int; 
// try to acquire the MDL EXCLUSIVE lock, wait for session 1 to release lock

Same as previous experiment, the DDL statement waits.

session 1:

mysql> insert into account () values ();
Query OK, 1 row affected (0.00 sec)

There is no additional lock needed. Session 1 already has the MDL SHARED_WRITE lock it needs to do the insert, so it is able to proceed.

huangapple
  • 本文由 发表于 2023年6月16日 01:24:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76484118.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定