文档

MDL优化(元数据锁)

更新时间:

MDL锁(MetaData Lock,元数据锁)是数据库的内部锁,用于确保执行DDL时事务使用的表元数据的一致性。普通读写事务需要获取对应表的MDL读锁,DDL操作需要获取对应表的MDL写锁。执行DDL时需要关注的MDL锁相关问题如下:

  • 阻塞性

    长事务长期持有的MDL读锁会阻塞DDL的执行,这会使DDL因长时间申请MDL写锁不成功而失败。此外,由于MySQL中MDL锁是公平锁,当DDL因为长事务持有MDL读锁而阻塞等待MDL写锁时,新事务也会因排队申请MDL读锁而将被全部阻塞。

  • 死锁风险

    多个DDL和事务一起执行时,由于申请MDL锁的顺序不同,可能引发MDL死锁。

  • 排它性

    MDL写锁具有排它性,当DDL获取到MDL写锁后,所有新来的事务都将因为无法申请MDL读锁而等待,这会造成流量跌零。虽然MySQL支持了Online DDL,但这类DDL只是尽力避免全程锁表,在一些关键的环节仍需要短暂获取MDL写锁。

执行DDL时一旦出现上述问题,可能导致业务连接的堆积和阻塞,严重时可造成业务流量的暂时跌零,后果较为严重。PolarDB-X对上述MDL锁的问题进行了针对性优化,使得执行DDL时不再需要担心上述风险。

抢占式MDL锁优化

PolarDB-X支持抢占式MDL锁优化,该优化消除了执行DDL时由于MDL锁的阻塞性导致的潜在风险。

支持版本

PolarDB-X的5.4.17-16952556及以上版本支持该功能。

详细说明

执行需要申请MDL写锁的DDL时,如果申请MDL写锁的等锁时间过长,PolarDB-X将自动停止阻塞该DDL的长事务所属的连接。

下表展示了没有抢占式MDL锁优化时,DDL和新事务被旧长事务阻塞的情形。

表1

步骤

Session1

Session2

Session3

1

begin;

-

begin;

2

insert into tb0 values(1);

-- 获取tb0的MDL读锁

-

-

3

-- 长期不提交,模拟长事务

-

-

4

-

alter table tb0 add column col int;

-- 尝试获取tb0的MDL写锁,被长事务阻塞

-

5

-

-

select id from tb0;

-- 尝试获取tb0的MDL读锁,被阻塞。

抢占式MDL锁优化后的执行过程如下(步骤与表1完全一致,关键步骤已在图中标出):

image.png

可以看到,由于抢占式MDL锁优化机制,session1上的长事务所属连接被断开,保证了session2上的DDL顺利执行,也确保了Session3上的新事务的正常执行。

分布式MDL死锁检测

PolarDB-X支持分布式MDL死锁检测,该功能可以检测并打破在执行DDL时由MDL锁参与形成的死锁,维护DDL和事务的正常执行。

支持版本

PolarDB-X的5.4.17-16952556及以上版本支持该功能。

详细说明

PolarDB-X将定时扫描事务和DDL之间的锁等待情况,如果检测到死锁,PolarDB-X将自动停止一个普通事务,以确保DDL和其它事务的正常执行。

下表展示了一个典型的由MDL锁参与的死锁场景。

表2

步骤

session1

session2

session3

session4

1

begin;

-- 开启事务1

begin;

-- 开启事务2

-

-

2

insert into t1 values(1);

-- 获取t1的MDL读锁

insert into t2 values(1);

-- 获取t2的MDL读锁

-

-

3

-

-

alter table t1 add column col int;

-- 发起DDL1,尝试获取t1的MDL写锁,被阻塞

alter table t2 add column col int;

-- 发起DDL2,尝试获取t2的MDL写锁,被阻塞

4

insert into t2 values(2);

-- 尝试获得t2的MDL读锁。但因为MDL是公平锁,所以被DDL2阻塞

insert into t1 values(2);

-- 尝试获得t1的MDL读锁。但因为MDL是公平锁,所以被DDL1阻塞

-

-

如下演示了在具有分布式MDL死锁检测功能的PolarDB-X上,表2中的死锁问题如何得到解决(执行步骤与上表完全一致,关键步骤已在图中标出):

image.png

在按照表2里的顺序生成死锁场景后,PolarDB-X检测到了死锁,并选择断开事务2所在的连接,事务2被回滚,事务1和DDL1、DDL2顺利执行。

双版本MDL锁优化

对于逻辑执行的DDL,PolarDB-X支持了双版本元数据和对应的双版本MDL锁,这使得在执行这类DDL时,真正可以做到全程不锁表、流量不跌零。

限制条件

该优化支持PolarDB-X中所有逻辑执行的DDL,判断DDL是否为逻辑执行。详情请参见Online DDL

详细说明

PolarDB-X的逻辑DDL基于Online Schema Change原理实现,将一个逻辑DDL的元数据版本细分为多个小版本,使得DDL的元数据版本演进可以在PolarDB-X实例中安全进行。例如PolarDB-X中的CREATE GLOBAL INDEX DDL全程会涉及ABSENT(Vn),DELETE_ONLY(Vn+1),WRITE_ONLY(Vn+2),PUBLISH(Vn+3)多次版本切换。此外,PolarDB-X将MDL锁与元数据的小版本绑定,每个版本的元数据都拥有各自对应版本的元数据锁。

得益于Online Schema Change机制,在同一时刻,不仅集群中的不同CN节点之间允许存在两个版本,一个CN节点内部也允许存在两个版本。因此当逻辑DDL开始元数据版本演进时,每一次演进,PolarDB-X只会获取旧版本元数据对应的MDL写锁,因此新事务可以访问新版本元数据,并申请新版本元数据对应的MDL读锁。

在双版本元数据锁的优化下,PolarDB-X的逻辑DDL可以做到全程不锁表、流量不跌零。