
今天我们要给大家揭秘一个MySQL数据库里可能发生的“交通堵塞”现象——MDL LOCK堵塞。我们来一探究竟!

1案发现场LOCK TABLE READ VS INSERT INTO
案例大概的截图如下:

(点击可放大查看)
这里是测试出来的,线上当时也是一个lock table read的语句和一大批insert into的语句被堵塞,当然要恢复很简单,我们可以通过2种方式,来查杀堵塞源头,如下:
table 级别的MDL LOCK,除非手动发送lock table,那么其持续时间通常为事务级别,而innodb_trx中记录了全部的事务(只读和读写),因此我们可以通过innodb_trx查询事务持续时间长于processlist中“Waiting for table metadata lock”最长时间的session的事务通常就是堵塞源头(当然也有例外,这个以后再讨论)。
访问sys.schema_table_lock_waits进行判断,如果为5.7需要手动开启MDL LOCK的instrument,并且需要注意本视图只能检查table级别的MDL LOCK,也就是本例中的“Waiting for table metadata lock”。
2测试堵塞:谁动了我的INSERT?
但是在测试中,我们发现如下的执行顺序insert是可以执行,
CREATE TABLE `t1` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`)
)
mysql> select * from t1;
+----+------+------+
| id | a | b |
+----+------+------+
| 1 | 1 | 1 |
| 3 | 3 | 3 |
| 5 | 5 | 5 |
+----+------+------+

查看session状态如下:
mysql> show processlist;
+----+-----------------+-----------+------+---------+------+---------------------------------+--------------------+-----------+---------------+
| Id | User | Host | db | Command | Time | State | Info | Rows_sent | Rows_examined |
+----+-----------------+-----------+------+---------+------+---------------------------------+--------------------+-----------+---------------+
| 1 | event_scheduler | localhost | NULL | Daemon | 1046 | Waiting on empty queue | NULL | 0 | 0 |
| 3 | root | localhost | new | Query | 0 | starting | show processlist | 0 | 0 |
| 4 | root | localhost | new | Query | 392 | Waiting for table metadata lock | lock table t1 read | 0 | 0 |
| 5 | root | localhost | new | Sleep | 341 | | NULL | 0 | 0 |
+----+-----------------+-----------+------+---------+------+---------------------------------+--------------------+-----------+---------------+
(左右滑动查看更多)
这里可以看到这里只有lock table read的S2处于堵塞状态,而S3的insert的语句并没有堵塞,那么案例中的insert堵塞语句是哪里来的呢?

查看session状态如下:
+----+-----------------+-----------+------+---------+------+---------------------------------+-------------------------------+-----------+---------------+
| Id | User | Host | db | Command | Time | State | Info | Rows_sent | Rows_examined |
+----+-----------------+-----------+------+---------+------+---------------------------------+-------------------------------+-----------+---------------+
| 1 | event_scheduler | localhost | NULL | Daemon | 1325 | Waiting on empty queue | NULL | 0 | 0 |
| 3 | root | localhost | new | Query | 0 | starting | show processlist | 0 | 0 |
| 4 | root | localhost | new | Sleep | 671 | | NULL | 0 | 0 |
| 5 | root | localhost | new | Query | 4 | Waiting for table metadata lock | insert into t1 values( 9,9,9) | 0 | 0 |
+----+-----------------+-----------+------+---------+------+---------------------------------+-------------------------------+-----------+---------------
(左右滑动查看更多)
这个时候因为lock table read执行成功了,insert语句继续插入行则被MDL LOCK堵塞了。
3问题汇总和分析
问题1:为什么模拟中lock table table read堵塞后,insert可以执行?