当我执行alter table
命令时,我会遇到很多死锁。where 命令位于waiting for metadata lock
不属于 alter table 语句的表上。这些表通过外键链接,但我们没有更改外键。
有没有办法在不让mysql屈服的情况下运行alter tables?
最小的例子
给定这两个表表有外键关系。
create table parent(id int primary key);
create table child(
id int primary key,
`parent_id` int,
constraint `parent_id_fk` foreign key (`parent_id`) references `parent` (`id`)
);
当连接读取一些数据时,它会获取元数据锁。
start transaction read only;
select * from child;
-- commit later
然后一个单独的连接尝试在父表上运行一个 alter 语句。即使我们没有接触孩子或 id,这也需要对孩子进行元数据锁定。
alter table parent add column x int null;
我们可以看到这些锁
mysql> select * from performance_schema.metadata_locks ;
+-------------+--------------------+----------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
| OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | COLUMN_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_DURATION | LOCK_STATUS | SOURCE | OWNER_THREAD_ID | OWNER_EVENT_ID |
+-------------+--------------------+----------------+-------------+-----------------------+---------------------+---------------+-------------+--------------------+-----------------+----------------+
| TABLE | performance_schema | metadata_locks | NULL | 47604062188640 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6213 | 24981 | 15 |
| GLOBAL | NULL | NULL | NULL | 47604063432464 | INTENTION_EXCLUSIVE | STATEMENT | GRANTED | sql_base.cc:5412 | 23547 | 607 |
| BACKUP LOCK | NULL | NULL | NULL | 47604007764640 | INTENTION_EXCLUSIVE | TRANSACTION | GRANTED | sql_base.cc:5419 | 23547 | 607 |
| SCHEMA | test | NULL | NULL | 47604063432224 | INTENTION_EXCLUSIVE | TRANSACTION | GRANTED | sql_base.cc:5399 | 23547 | 607 |
| TABLE | test | parent | NULL | 47604068904032 | SHARED_UPGRADABLE | TRANSACTION | GRANTED | sql_parse.cc:6213 | 23547 | 607 |
| TABLESPACE | NULL | test/parent | NULL | 47604068895072 | INTENTION_EXCLUSIVE | TRANSACTION | GRANTED | lock.cc:793 | 23547 | 607 |
| SCHEMA | test | NULL | NULL | 47604068902992 | INTENTION_EXCLUSIVE | STATEMENT | GRANTED | sql_table.cc:1117 | 23547 | 607 |
| TABLE | test | child | NULL | 47604007764800 | SHARED_UPGRADABLE | STATEMENT | GRANTED | sql_table.cc:1109 | 23547 | 607 |
| TABLE | test | #sql-1f53_5ad9 | NULL | 47604063517984 | EXCLUSIVE | STATEMENT | GRANTED | sql_table.cc:16153 | 23547 | 607 |
| TABLE | test | child | NULL | 47604063429264 | EXCLUSIVE | STATEMENT | PENDING | sql_table.cc:1109 | 23547 | 608 |
| TABLE | test | child | NULL | 47604010642320 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6213 | 24466 | 120 |
生产中的死锁
在我们有更多表和外键的生产系统中,我们看到死锁,其中更改表为表 A 获取锁但无法获取表 B。表 B 被需要表 A 的线程锁定。对我们来说有点竞争条件,但由于 mysql 需要锁定所有链接表而不仅仅是一个正在改变的表,从而加剧了这种情况。
这似乎是 mysql 8 的新行为。我希望我没有升级。我们在带有 innodb 表的 mysql 8.0.20 上。
有没有办法在不锁定数据库的情况下运行这些更改?理想情况下,我想保留我的外键,我不想使用像 Percona 这样的迁移管理器。我的桌子甚至没有那么大。
谢谢