2

当我执行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 这样的迁移管理器。我的桌子甚至没有那么大。

谢谢

4

1 回答 1

0

要使您的 ALTER TABLE 语句不进行锁定,或者如果在没有锁定的情况下无法执行操作,则返回错误,请执行以下操作:

ALTER TABLE ..., LOCK=NONE;
于 2020-10-14T03:29:56.940 回答