3

When I do alter table commands Im getting lots of dead locks. Where commands are waiting for metadata lock on tables that are not part of the alter table statement. The tables are linked through a foreign key but we are not changing the foreign key.

Is there a way to run alter tables without bringing mysql to its knees?

Minimal Example

Given these two tables tables with a foreign key relationship.

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`)
);

When a connection reads some data it acquires a metadata locks.

start transaction read only;
select * from child;
-- commit later

A seperate connection then tries to run an alter statement on the parent table. This wants a metadata lock on child even though we are not touching child or the id.

alter table parent add column x int null;

We can see these locks in

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 |

Dead locks in production

In our production system which has more tables and foreign keys we are seeing dead locks, where the alter table acquires a locks for table A but cant get table B. Table B is locked by a thread that needs table A. For us its a bit of a race condition, but its exacerbated by mysql needing a lock on all of the linked tables instead of just the one is is altering.

This seems to be new behaviour of mysql 8. I wish I didnt upgrade. We are on mysql 8.0.20 with innodb tables.

Is there a way to run these alters without locking up the database ? Ideally I want to keep my foreign keys and I dont want to use a migration manager like Percona. My tables arent even that big.

Thanks

Stephen
  • 4,228
  • 4
  • 29
  • 40

1 Answers1

0

To make your ALTER TABLE statement do no locking, or return an error if the operations being performed are not possible without locking, do:

ALTER TABLE ..., LOCK=NONE;
ysth
  • 96,171
  • 6
  • 121
  • 214
  • Hi, this doesnt seem to work in this case. It seems to ignore it. Im not sure the reason maybe it ignores metadata locks ? – Stephen Oct 14 '20 at 03:57
  • huh; does replacing the `*` with a list of columns help? I don't have any other ideas – ysth Oct 14 '20 at 04:58
  • 1
    yeah, maybe switch to a better database, PostgreSQL has tools to help :-) https://wiki.postgresql.org/wiki/Converting_from_other_Databases_to_PostgreSQL . locks are used on alters but it's well documented and there are fairly easy work arounds – Karl Oct 14 '20 at 22:50