前言
想必玩过mysql的人对Waiting for table metadata lock肯定不会陌生,一般都是进行alter操作时被堵住了,导致了我们在show processlist 时,看到线程的状态是在等metadata lock。本文会对MySQL表结构变更的Metadata Lock进行详细的介绍。
在线上进行DDL操作时,相对于其可能带来的系统负载,其实,我们最担心的还是MDL其可能导致的阻塞问题。
一旦DDL操作因获取不到MDL被阻塞,后续其它针对该表的其它操作都会被阻塞。典型如下,如阻塞稍久的话,我们会看到Threads_running飙升,CPU告警。
mysql> show processlist;+----+-----------------+-----------+-----------+---------+------+---------------------------------+------------------------------------+| Id | User | Host | db | Command | Time | State | Info |+----+-----------------+-----------+-----------+---------+------+---------------------------------+------------------------------------+| 4 | event_scheduler | localhost | NULL | Daemon | 122 | Waiting on empty queue | NULL || 9 | root | localhost | NULL | Sleep | 57 | | NULL || 12 | root | localhost | employees | Query | 40 | Waiting for table metadata lock | alter table slowtech.t1 add c1 int || 13 | root | localhost | employees | Query | 35 | Waiting for table metadata lock | select * from slowtech.t1 || 14 | root | localhost | employees | Query | 30 | Waiting for table metadata lock | select * from slowtech.t1 || 15 | root | localhost | employees | Query | 19 | Waiting for table metadata lock | select * from slowtech.t1 || 16 | root | localhost | employees | Query | 10 | Waiting for table metadata lock | select * from slowtech.t1 || 17 | root | localhost | employees | Query | 0 | starting | show processlist |+----+-----------------+-----------+-----------+---------+------+---------------------------------+------------------------------------+rows in set (0.00 sec) |
如果发生在线上,无疑会影响到业务。所以,一般建议将DDL操作放到业务低峰期做,其实有两方面的考虑,1. 避免对系统负载产生较大影响。2. 减少DDL被阻塞的概率。
MDL引入的背景
MDL是MySQL 5.5.3引入的,主要用于解决两个问题,
RR事务隔离级别下不可重复读的问题
如下所示,演示环境,MySQL 5.5.0。
session1> begin;Query OK, 0 rows affected (0.00 sec)session1> select * from t1;+------+------+| id | name |+------+------+| 1 | a || 2 | b |+------+------+rows in set (0.00 sec)session2> alter table t1 add c1 int;Query OK, 2 rows affected (0.02 sec)Records: 2 Duplicates: 0 Warnings: 0session1> select * from t1;Empty set (0.00 sec)session1> commit;Query OK, 0 rows affected (0.00 sec)session1> select * from t1;+------+------+------+| id | name | c1 |+------+------+------+| 1 | a | NULL || 2 | b | NULL |+------+------+------+rows in set (0.00 sec) |