A:metadata lock是表级锁,是在server层加的,适用于所有存储引擎。所有的dml操作都会在表上加一个metadata读锁;所有的ddl操作都会在表上加一个metadata写锁。读锁和写锁的阻塞关系如下:
读锁和写锁之间相互阻塞,即同一个表上的dml和ddl之间互相阻塞。写锁和写锁之间互相阻塞,即两个session不能对表同时做表定义变更,需要串行操作。读锁和读锁之间不会产生阻塞。也就是增删改查不会因为metadata lock产生阻塞,可以并发执行,日常工作中大家看到的dml之间的锁等待是innodb行锁引起的,和metadata lock无关。
熟悉innodb行锁的同学这里可能有点困惑,因为行锁分类和metadata lock很类似,也主要分为读锁和写锁,或者叫共享锁和排他锁,读写锁之间阻塞关系也一致。二者最重要的区别一个是表锁,一个是行锁,且行锁中的读写操作对应在metadata lock中都属于读锁。
大家也许会奇怪,以前听说普通查询不加锁的,怎么这里又说要加表锁,我们做一个简单测试:
session1:查询前,先看一下metadata_locks表,这个表位于performance_schema下,记录了metadata lock的加锁信息。
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 | 139776223308432 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6014 | 54 | 12 |+-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+-------------------+-----------------+----------------+1 row in set (0.00 sec)
session2:执行简单查询,为了让表处于执行状态,这里使用了sleep函数。
mysql> select sleep(10) from t1;+-----------+| sleep(10) |+-----------+| 0 || 0 || 0 |+-----------+3 rows in set (30.00 sec)
session1:
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 | db1 | t1 | NULL | 139776154308336 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6014 | 53 | 22 || TABLE | performance_schema | metadata_locks | NULL | 139776223308432 | SHARED_READ | TRANSACTION | GRANTED | sql_parse.cc:6014 | 54 | 13 |+-------------+--------------------+----------------+-------------+-----------------------+-------------+---------------+-------------+-------------------+-----------------+----------------+2 rows in set (0.00 sec)
此时再次查看metadata_lock表,发现多了一条t1的加锁记录,加锁类型为SHARED_READ,且状态是已授予(GRANTED)。大家通常理解的查询不加锁,是指不在表上加innodb行锁。
如果在执行sleep期间,另外一个session执行了一个加字段操作,此时就会产生metadata lock锁等待:
session2:
mysql> select sleep(10) from t1;
执行中......
session3:
mysql> alter table t1 add col1 int;
阻塞中......
session1:
mysql> show processlist;+----+-----------------+-----------+------+---------+--------+---------------------------------+-----------------------------+| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+------+---------+--------+---------------------------------+-----------------------------+| 4 | event_scheduler | localhost | NULL | Daemon | 861577 | Waiting on empty queue | NULL || 18 | root | localhost | db1 | Sleep | 50 | | NULL || 19 | root | localhost | NULL | Query | 0 | starting | show processlist || 20 | root | localhost | db1 | Query | 11 | Waiting for table metadata lock | alter table t1 add col1 int |+----+-----------------+-----------+------+---------+--------+---------------------------------+-----------------------------+4 rows in set (0.00 sec)
显然,id为20的线程还未执行alter操作,状态为‘Waiting for table metadata lock’,也就是在等待session2的sleep操作完成。