3. Preemptive DDL (饥饿问题)
3.1 功能概述
上文非阻塞DDL解决了DDL获取MDL锁阻塞导致的业务雪崩问题,但是如果DDL迟迟无法获取MDL锁,会导致DDL执行频繁失败。目前线上值班偶尔会遇到由于RO上面存在大查询、长事务导致的DDL执行失败问题,并返回错误ERROR 8007 (HY000): Fail to get MDL on replica during DDL synchronize。由于此报错与PolarDB共享存储的架构相关,与传统MySQL不一致,用户经常会一头雾水,无从下手。当前已有官方文档(执行DDL操作提示“获取不到MDL锁”)介绍这类问题的解决方案,用户可以根据此文档找到只读节点上持有表MDL锁的事务,手动进行Kill,来保证DDL同步MDL锁的成功。但是这种方式在部分场景下依然非常晦涩,一方面用户进行kill操作的时间窗口有限(当前同步MDL锁超时时间为50秒,可通过loose_replica_lock_wait_timeout进行调整),另一方面随着PolarDB上面客户不断增多,出现了许多10+个只读节点的集群,手动kill操作显得狼狈且低效,为此我们提供了抢占式DDL功能。
当只读节点通过物理复制,解析到当前表上有DDL操作时,只读节点会尝试获取表的MDL锁。如果此时表上存在大查询或长事务时,开启Preemptive DDL后(用户文档),如果只读节点在预期时间内无法获得MDL锁,便会尝试kill掉占有MDL锁的线程,从而保证MDL锁同步的成功,解决DDL的饥饿问题。
3.2 测试效果
可以通过设置参数loose_polar_support_mdl_sync_preemption为ON来打开抢占式DDL功能,下面给出DDL同步MDL锁被只读节点长事务堵塞时,开启和关闭抢占式DDL的实验效果。
- 关闭抢占式DDL功能
- 在只读节点上查询test.t1:
mysql> use test
Database changed
#大查询,执行100s
mysql> select sleep(100) from t1;
- 在主节点进行加列操作,被block,执行失败:
mysql > alter table t1 add column c int;
ERROR 8007 (HY000): Fail to get MDL on replica during DDL synchronize
由于只读节点存在大查询,同步MDL锁失败,DDL执行失败,并回滚。
- 开启抢占式DDL功能
- 在只读节点上查询test.t1:
mysql> use test
Database changed
#大查询,执行100s
mysql> select sleep(100) from t1;
- 在主节点进行加列操作操作,被block,等待一段时间,发生抢占,执行成功:
mysql> alter table t1 add column c int;
Query OK, 0 rows affected (11.13 sec)
Records: 0 Duplicates: 0 Warnings: 0

开启抢占式DDL功能后,加列操作完成,同时可以看到只读节点(右图),大查询连接已经断开。





