英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论