tencent cloud

TDSQL Boundless

抢占式 DDL

PDF
聚焦模式
字号
最后更新时间: 2026-03-26 15:19:24

功能概述

TDSQL Boundless 提供抢占式 DDL 功能,用于解决分布式场景下, DDL 操作因其他节点上的长事务持有 MDL-S 锁而长时间等待甚至超时失败的问题。
开启抢占式 DDL 后,当 DDL 操作在目标节点等待 MDL-X 锁超过指定时间时,系统将主动终止阻塞 MDL-X 锁获取的会话,确保 DDL 操作能够顺利完成。

背景信息

TDSQL Boundless 分布式架构下的 DDL 锁协调

TDSQL Boundless 采用分布式架构,每个 SQLEngine 节点均具备独立的读写能力。当某个节点执行 DDL 操作时,需要在以下两个层面获取锁:
1. 本节点 MDL 锁: DDL 需要在发起节点获取目标表的 MDL-X 锁,与本节点上的活跃事务互斥。
2. 全局对象锁: DDL 需要通过元数据服务(TDMC)获取全局对象锁,确保同一时间只有一个节点能修改表结构,防止跨节点 DDL 冲突。

问题现象

在同一节点上,如果存在对目标表的长事务或大查询,这些会话会持有该表的 MDL-S 锁。DDL 操作需要获取 MDL-X 锁,两者互斥,导致 DDL 被阻塞。如果长事务在锁等待超时时间内未结束,DDL 将执行失败。
典型报错:
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
在未开启抢占式 DDL 的情况下,处理此类问题通常需要 DBA 手动定位并终止持锁会话,操作流程繁琐且响应不及时。

抢占式 DDL 的解决思路

抢占式 DDL 提供了一种自动化的解决方案:当 DDL 等待 MDL 锁超过指定时间后,系统将自动终止阻塞 DDL 的会话,释放 MDL 锁资源,使 DDL 操作能够继续执行。相比手动干预,这种方式响应更快、操作更可靠。

注意事项

业务影响: 开启抢占式 DDL 后,阻塞 DDL 的会话将被系统强制终止,会话上未提交的事务将被回滚。请评估业务对事务中断的容忍度。
建议在低峰期使用: 尽管抢占式 DDL 能自动处理锁冲突,仍建议在业务低峰期执行 DDL 变更,以减少对正常业务的影响。

参数说明

通过以下参数控制抢占式 DDL 的行为,可在控制台的参数配置页面或通过 SQL 命令进行设置。
参数
级别
说明
取值范围
默认值
tdsql_ddl_block_mode
Session
控制普通线程(normal)DDL 锁获取行为,设置为 preemptive 时启用抢占式 DDL 逻辑
preemptive
nonblock
default
preemptive
tdsql_ddl_recovery_block_mode
Global
控制恢复线程(recovery)DDL 锁获取行为,设置为 preemptive 时启用抢占式 DDL 逻辑;仅 GLOBAL 级别设置生效,SESSION 级别设置无效
preemptive
nonblock
default
preemptive
tdsql_ddl_preempt_after_wait_seconds
Session
抢占式 DDL 等待 MDL‑X 锁的容忍时间,超过该时间后自动触发锁抢占流程。单位:秒
1 ~ 31536000
50

使用方法

1. 开启抢占式 DDL。
在执行 DDL 的会话中开启功能:
SET tdsql_ddl_block_mode = 'preemptive';
-- tdsql_ddl_block_mode参数的默认值即为 'preemptive'
2. (可选)调整抢占等待时间。
例如设置为5秒:
SET tdsql_ddl_preempt_after_wait_seconds = 5;
3. (可选)配置恢复线程抢占式 DDL。
SET GLOBAL tdsql_ddl_recovery_block_mode = 'preemptive';
4. 执行 DDL 操作。
ALTER TABLE orders ADD COLUMN remark VARCHAR(255);
如果当前节点上存在长事务阻塞了该表的 MDL 锁,系统将在等待指定时间后自动终止阻塞会话,DDL 继续执行直至完成。

使用示例

准备工作

创建测试表并插入数据:
CREATE TABLE t1 (id INT PRIMARY KEY, name VARCHAR(50));
INSERT INTO t1 VALUES (1, 'Alice'), (2, 'Bob');

未开启抢占式 DDL

会话1(模拟长事务):
BEGIN;
SELECT * FROM t1;
-- 事务未提交,持有 t1 的 MDL 共享读锁
会话2(执行 DDL):
ALTER TABLE t1 ADD COLUMN age INT;
-- 等待 MDL 排他锁...
-- 超时后报错:
-- ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
结果: DDL 执行失败,需要 DBA 手动通过 performance_schema.metadata_locks 定位持锁会话。

开启抢占式 DDL

会话1(模拟长事务):
BEGIN;
SELECT * FROM t1;
-- 事务未提交,持有 t1 的 MDL 共享读锁
会话2(开启抢占式 DDL 并执行):
SET tdsql_ddl_block_mode = 'preemptive';
SET tdsql_ddl_preempt_after_wait_seconds = 5;
ALTER TABLE t1 ADD COLUMN age INT;
-- 等待 5 秒后,系统自动触发抢占并终止阻塞会话
Query OK, 0 rows affected (5.067 sec)
Records: 0 Duplicates: 0 Warnings: 0
会话1的变化:
SELECT * FROM t1;
ERROR 2013 (HY000): Lost connection to MySQL server during query
结果: DDL 执行成功。会话1被终止,事务被回滚,后续操作需重新发起。

排查与诊断

当 DDL 操作遇到 MDL 锁阻塞时,您也可以通过以下方式手动排查,作为抢占式 DDL 的补充手段:
1. 查看 MDL 锁持有情况:
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_NAME = 't1';
2. 定位持锁会话并参考 PROCESSLIST_ID 手动终止持锁会话:
SELECT
ml.OBJECT_NAME,
ml.LOCK_TYPE,
ml.LOCK_STATUS,
t.PROCESSLIST_ID,
t.PROCESSLIST_INFO
FROM performance_schema.metadata_locks ml
JOIN performance_schema.threads t
ON ml.OWNER_THREAD_ID = t.THREAD_ID
WHERE ml.OBJECT_NAME = 't1' AND ml.LOCK_STATUS = 'GRANTED';
说明:
在分布式场景下,如果 DDL 报错 ERROR 8542 ... Acquire object lock ... wait timeout,表明 DDL 被其他节点的 DDL 通过全局对象锁阻塞,此场景属于 DDL-DDL 冲突,需等待另一个 DDL 完成,抢占式 DDL 不适用于此场景。

相关文档

帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈