RDS for Mysql的MDL锁视图
1、MDL锁概念
MDL全称为metadata lock,即元数据锁。官方对MDL锁的定义如下:
MySQL uses metadata locking to manage concurrent access to database objects and to ensure data consistency. Metadata locking applies not just to tables, but also to schemas, stored programs (procedures, functions, triggers, scheduled events), tablespaces, user locks acquired with the GET_LOCK() function, and locks acquired with the locking service.
从定义可以看出MDL锁主要作用是维护表元数据(包括 table,schema, function, procedure, trigger, event等)的数据一致性,在表上有活动事务(显式或隐式)的时候,不可以对元数据进行写入操作。主要解决了2个问题,一个是事务隔离问题,比如在可重复隔离级别下,会话A在2次查询期间,会话B对表结构做了修改,两次查询结果就会不一致,无法满足可重复读的要求;另外一个是数据复制的问题,比如会话A执行了多条更新语句期间,另外一个会话B做了表结构变更并且先提交,就会导致slave在重做时,先重做alter,再重做update时就会出现复制错误的现象。
2、社区版MDL锁的限制
MDL锁通常发生在DDL操作挂起的时候,原因是有未提交的事务对该表进行DML操作。而MySQL的会话那么多,不知道哪个会话的操作没有及时提交影响了DDL。社区版MySQL如果不打开performance_schema开关,则无法获取表元数据锁(MDL)的详细信息,通常我们排查这类问题,往往需要从information_schema.innodb_trx表中查询当前在执行的事务,但当SQL已经执行过了,没有commit,这个时候这个表中是看不到SQL的。当用户遇到类似“Waiting for metadata lock”的问题而阻塞DML或DDL后,由于无法确定各个会话之间的关联关系,只能重启实例,增加了解决问题的成本,对业务产生了较大影响。在业务场景较复杂的情况下,一旦涉及对数据库元数据的互斥操作(如DDL、LOCK TABLE等),会频繁发生操作的会话被其他会话阻塞的问题,给用户带来很大的困扰。
3、华为云RDS For Mysql的MDL锁视图
华为云RDS for MySQL推出了MDL锁视图特性,可以查看数据库各会话持有和等待的元数据锁信息,用户可以有效进行系统诊断,优化自身业务,有效降低对业务影响。
MDL锁视图以系统表的形式呈现,该表位于“information_schema”下,表名称是“metadata_lock_info”。表结构如下所示。
desc information_schema.metadata_lock_info;
+---------------+-----------------------+---------+------+----------+--------|
| Field | Type | Null |Key |Default | Extra |
+---------------+-----------------------+---------+------+----------+--------|
| THREAD_ID | bigint(20) unsigned | NO | | 0 | |
| LOCK_STATUS | varchar(24) | NO | | | |
| LOCK_MODE | varchar(24) | YES | | NULL | |
| LOCK_TYPE | varchar(30) | YES | | NULL | |
| LOCK_DURATION | varchar(30) | YES | | NULL | |
| TABLE_SCHEMA | varchar(64) | YES | | NULL | |
| TABLE_NAME | varchar(64) | YES | | NULL | |
+---------------+-----------------------+---------+------+----------+--------|
表1 metadata_lock_info字段
序号 |
字段名 |
字段定义 |
字段说明 |
---|---|---|---|
0 |
THREAD_ID |
bigint(20) unsigned |
会话ID。 |
1 |
LOCK_STATUS |
varchar(24) |
MDL锁的两种状态。
|
2 |
LOCK_MODE |
varchar(24) |
加锁的模式,如MDL_SHARED 、MDL_EXCLUSIVE 、MDL_SHARED_READ、MDL_SHARED_WRITE等。 |
3 |
LOCK_TYPE |
varchar(30) |
MDL锁的类型,如Table metadata lock、Schema metadata lock、Global read lock、Tablespace lock等。 |
4 |
LOCK_DURATION |
varchar(30) |
MDL锁范围,取值如下:
|
5 |
TABLE_SCHEMA |
varchar(64) |
数据库名,对于部分GLOBAL级别的MDL锁,该值为空。 |
6 |
TABLE_NAME |
varchar(64) |
表名,对于部分GLOBAL级别的MDL锁,该值为空。 |
MDL锁视图使用示例
使用场景:长时间未提交事务,阻塞DDL,继而阻塞所有同表的操作。
表名 |
会话 |
|||
session2 |
session3 |
session4 |
session5 |
|
t1 |
begin; select * from t1; |
- |
- |
- |
t2 |
- |
begin; select * from t2; |
- |
- |
t3 |
- |
- |
truncate table t2; (blocked) |
- |
t4 |
- |
- |
- |
begin; select * from t2; (blocked) |
MDL锁视图案例分析
问题描述
用户发现对表t2执行truncate操作一直被阻塞后,业务流程中对表t2执行查询操作也全部被阻塞。
排查分析
- 无MDL锁视图
当发现DDL语句被阻塞后,执行show processlist查看线程信息,结果如下所示。
show processlist; +------+--------+--------------+--------+-----------+----------+-----------------------------------|-------------------------| | Id | User | Host | db | Command | Time | State |Info | +---------------+-----------------------+-----------+----------+-----------------------------------+-------------------------| | 2 | root | localhost | test | Sleep | 73 | | Null | | 3 | root | localhost | test | Sleep | 63 | | Null | | 4 | root | localhost | Null | Query | 35 | Waiting for table metadata lock | truncate table test.t2 | | 5 | root | localhost | test | Query | 17 | Waiting for table metadata lock | select * from test.t2 | | 6 | root | localhost | test | Query | 0 | starting | show processlist | +------+--------+--------------+--------+-----------+----------+-----------------------------------|-------------------------|
上述线程列表信息显示:
- ID=4的会话执行truncate操作时被其他会话持有的table metadata lock阻塞。
- ID=5的会话执行查询操作时同样被阻塞。
- 无法确定哪个会话阻塞了ID=4的会话和ID=5的会话。
此时,如果随机KILL其他会话会给线上业务带来很大风险,因此只能等待其他会话释放该MDL锁。
- 使用MDL锁视图
执行select * from information_schema.metadata_lock_info查看元数据锁信息,结果如下所示。
select * from information_schema.metadata_lock_info; +-------------+-------------+--------------------------+----------------------+-------------------+----------------+----------------+ | THREAD_ID | LOCK_STATUS | LOCK_MODE | LOCK_TYPE | LOCK_DURATION | TABLE_SCHEMA | TABLE_NAME | +-------------+-------------+--------------------------+----------------------+-------------------+----------------+----------------+ | 2 | GRANTED | MDL_SHARED_READ | Table metadata lock | MDL_TRANSACTION | test | t1 | | 3 | GRANTED | MDL_SHARED_READ | Table metadata lock | MDL_TRANSACTION | test | t2 | | 4 | GRANTED | MDL_INTENTION_EXCLUSIVE | Global read lock | MDL_STATEMENT | | | | 4 | GRANTED | MDL_INTENTION_EXCLUSIVE | Schema metadata lock | MDL_TRANSACTION | test | | | 4 | PENDING | MDL_EXCLUSIVE | Table metadata lock | | test | t2 | | 5 | PENDING | MDL_SHARED_READ | Table metadata lock | | test | t2 | +-------------+-------------+--------------------------+----------------------+-------------------+----------------+----------------+
结合show processlist的结果,从元数据锁视图中可以明显看出:
上述线程信息和元数据锁视图信息显示:
- THREAD_ID=4的会话正在等待表t2的metadata lock。
- THREAD_ID=3的会话持有表t2的metadata lock,该MDL锁为事务级别,因此只要THREAD_ID=3的会话的事务不提交,THREAD_ID=4的会话将会一直阻塞。
因此,用户只需在THREAD_ID=3的会话中执行命令commit或终止THREAD_ID=3的会话,便可以让业务继续运行。
MDL锁视图的优点:可以让客户和一线运维人员清晰地查看数据库各session持有和等待的元数据锁信息,从而找出数据库MDL锁等待的根因,准确地进行下一步决策,有效降低对业务的影响。
- 点赞
- 收藏
- 关注作者
评论(0)