tencent cloud

TDSQL Boundless

产品动态
产品简介
产品概述
应用场景
产品架构
实例类型
兼容性说明
使用规范建议
自研内核
内核概述
内核版本更新动态
功能类特性
性能类特性
产品计费
计费概述
购买方式
价格详情
续费说明
欠费说明
退费说明
快速入门
创建实例
连接实例
用户指南
数据迁移
数据同步/订阅
实例管理
参数配置
账号管理
安全组
备份与恢复
数据库审计
标签管理
实践教程
Online DDL 的技术演进与使用实践
锁机制解析与问题排查实践
数据智能调度及相关性能优化实践
TDSQL Boundless 选型指南与实践教程
开发指南
开发指南(MySQL 兼容模式)
开发指南(HBase 兼容模式)
性能调优
性能调优概述
SQL 调优
DDL 调优
性能白皮书
性能概述
TPC-C 测试
Sysbench 测试
API 文档
History
Introduction
API Category
Making API Requests
Instance APIs
Security Group APIs
Task APIs
Backup APIs
Rollback APIs
Parameter APIs
Database APIs
Data Types
Error Codes
通用参考
系统原理
SQL 参考
数据库参数说明
TPC-H 基准测试数据模型参考
错误码信息
安全与合规
常见问题
服务协议
服务等级协议
服务条款
隐私政策
数据处理和安全协议
联系我们
词汇表

非阻塞 DDL

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

功能概述

TDSQL Boundless 提供非阻塞 DDL(Non-blocking DDL)功能,用于解决 DDL 操作在等待 MDL 排他锁期间阻塞目标表上所有新事务,导致业务连接堆积甚至系统崩溃的问题。
开启非阻塞 DDL 后,当 DDL 无法立即获取 MDL 排他锁时,系统将暂时释放锁请求,允许新事务正常进入目标表执行,然后以超时重试的方式多次尝试获取锁,直至 DDL 最终完成。

背景信息

DDL 等待 MDL 锁引发的连锁阻塞

在 TDSQL Boundless 中,DDL 操作(如 ALTER TABLE)需要获取目标表的 MDL 排他锁(MDL-X)。当目标表上存在未提交的长事务或大查询时,这些会话持有该表的 MDL 共享读锁,DDL 将进入等待状态。
在默认机制下,MDL 排他锁具有最高优先级。DDL 一旦进入等待队列,其后所有对该表的新事务(包括普通的 SELECTINSERTUPDATE)都将被阻塞——它们既无法获取 MDL 共享锁(因为等待队列中有优先级更高的排他锁请求),也无法绕过排他锁请求直接执行。
这种连锁阻塞会带来严重后果:
1. 业务连接快速堆积,数据库连接池耗尽。
2. 应用层出现大量超时错误,服务不可用。
3. 严重时可能导致整个业务系统崩溃。

非阻塞 DDL 的解决思路

非阻塞 DDL 改变了 MDL 排他锁的获取策略:
默认行为: DDL 提交排他锁请求后持续等待,期间阻塞所有新事务。
非阻塞行为: DDL 在短时间内尝试获取排他锁,如果获取失败,则主动放弃本次锁请求,让新事务正常通过。等待一段间隔后再次尝试,如此循环直至获取成功或达到最大重试次数。
这种"尝试 - 退让 - 重试"的机制确保了即使 DDL 无法立即完成,业务流量也不会被持续阻塞。

与抢占式 DDL 的区别

维度
抢占式 DDL
非阻塞 DDL
解决的问题
DDL 因长事务阻塞而执行失败
DDL 等待期间阻塞所有新事务
处理方式
超时后终止阻塞 DDL 的会话
超时后 DDL 主动退让,放行新事务
对已有会话的影响
持锁会话会被强制终止
不影响任何已有会话
DDL 执行结果
DDL 一定成功
DDL 可能因重试次数耗尽而失败
适用场景
可接受中断长事务,要求 DDL 必须成功
不可中断业务会话,要求业务零感知
两个功能可以根据业务需要单独使用,也可以配合使用。

注意事项

DDL 失败概率增大: 开启非阻塞 DDL 后,DDL 的锁优先级降低。如果目标表上持续有活跃事务,DDL 可能在所有重试次数耗尽后仍无法获取锁,最终执行失败。
DDL 执行时间延长: 由于采用重试机制,DDL 的总执行时间 = 实际 DDL 耗时 + 重试等待时间。在锁冲突严重的场景下,DDL 完成时间可能明显增加。

参数说明

通过以下参数控制非阻塞 DDL 的行为,可在执行 DDL 前动态设置。
参数
级别
说明
取值范围
默认值
tdsql_ddl_block_mode
Session
非阻塞 DDL 功能开关,设置为 nonblock 时启用非阻塞 DDL 逻辑。
preemptive
nonblock
default
preemptive
tdsql_ddl_recovery_block_mode
Global
控制恢复线程的非阻塞 DDL 行为,设置为 nonblock 时生效;仅 GLOBAL 级别设置生效,Session 级别设置无效
preemptive
nonblock
default
preemptive
tdsql_ddl_nonblock_lock_wait_timeout
Session
单次尝试获取 MDL-X 锁的超时时间。超过该时间未获取到锁,DDL 将主动放弃本次请求,允许新事务通过。单位:秒
1 ~ 102410241024
1
tdsql_ddl_nonblock_retry_interval
Session
两次锁获取尝试之间的等待间隔。单位:秒
0 ~ 60
4
tdsql_ddl_nonblock_retry_times
Session
获取 MDL-X 锁的最大重试次数。超过该次数后 DDL 将执行失败。
0 ~ ULONG_MAX
10
参数之间的关系:
DDL 的最大等待总时长约为:
总时长 ≈ tdsql_ddl_nonblock_retry_times × (tdsql_ddl_nonblock_lock_wait_timeout + tdsql_ddl_nonblock_retry_interval)

使用方法

1. 开启非阻塞 DDL。
SET tdsql_ddl_block_mode = 'nonblock';
2. (可选)配置重试参数。
例如,设置单次锁等待2秒,重试间隔5秒,最多重试20次:
SET tdsql_ddl_nonblock_lock_wait_timeout = 2;
SET tdsql_ddl_nonblock_retry_interval = 5;
SET tdsql_ddl_nonblock_retry_times = 20;
3. (可选)配置恢复线程非阻塞 DDL。
SET GLOBAL tdsql_ddl_recovery_block_mode = 'nonblock';
4. 执行 DDL 操作。
ALTER TABLE orders ADD INDEX idx_status (status);
DDL 执行过程中,如果遇到 MDL 锁冲突,系统将按照配置的参数进行"尝试 - 退让 - 重试",业务流量不受影响。

使用示例

下面将通过一个示例,演示非阻塞 DDL 开启前后对业务 TPS 的影响差异。

准备工作

CREATE TABLE sbtest1 (
id INT PRIMARY KEY,
k INT NOT NULL DEFAULT 0,
c CHAR(120) NOT NULL DEFAULT '',
pad CHAR(60) NOT NULL DEFAULT ''
);
-- 插入 100 万行测试数据(可通过 SysBench 等工具生成)
使用 SysBench 模拟持续业务负载:
sysbench oltp_read_write \\
--mysql-host="连接地址" \\
--mysql-port=3306 \\
--mysql-user="用户名" \\
--mysql-password="密码" \\
--mysql-db="test" \\
--tables=1 \\
--table-size=1000000 \\
--threads=8 \\
--time=600 \\
--report-interval=1 \\
run

模拟锁冲突

会话1(模拟未提交的长事务):
BEGIN;
SELECT * FROM sbtest1 LIMIT 1;
-- 不提交,持有 sbtest1 的 MDL 共享读锁

场景一:关闭非阻塞 DDL(默认行为)

会话2(执行 DDL):
ALTER TABLE sbtest1 ADD COLUMN d INT;
-- DDL 进入 MDL 锁等待队列
-- 此时所有新事务也被阻塞 → 业务 TPS 跌零
表现: 从 DDL 提交的瞬间开始,SysBench 的 TPS 持续跌零,直到长事务结束或 DDL 超时。业务完全中断。

场景二:开启非阻塞 DDL

会话2(开启非阻塞 DDL 后执行):
SET tdsql_nonblock_ddl_mode = ON;
SET tdsql_nonblock_ddl_lock_wait_timeout = 1;
SET tdsql_nonblock_ddl_retry_interval = 5;
SET tdsql_nonblock_ddl_retry_times = 50;

ALTER TABLE sbtest1 ADD COLUMN d INT;
-- DDL 尝试获取锁 → 1 秒后放弃 → 放行新事务 → 等待 5 秒 → 再次尝试...
-- 当会话 1 的事务结束后,DDL 在下一次重试时获取锁成功
-- Query OK, 0 rows affected (23.15 sec)
表现:Sysbench 的 TPS 出现周期性的短暂下降(每次 DDL 尝试获取锁时持续约1秒),但不会跌零。业务整体保持可用。

结果对比

场景
TPS 表现
业务影响
关闭非阻塞 DDL
TPS 持续跌零,直到锁释放
严重,业务完全中断
开启非阻塞 DDL
TPS 周期性短暂下降,不跌零
较小,业务保持可用

最佳实践

场景一:业务高峰期紧急加索引

业务高峰期需要紧急为慢查询添加索引,但表上有大量活跃事务:
-- 开启非阻塞 DDL,避免影响在线业务
SET tdsql_ddl_block_mode = 'nonblock';
SET tdsql_ddl_nonblock_lock_wait_timeout = 1;
SET tdsql_ddl_nonblock_retry_interval = 3;
SET tdsql_ddl_nonblock_retry_times = 100;

ALTER TABLE orders ADD INDEX idx_create_time (create_time);

场景二:配合抢占式 DDL 使用

对于关键的表结构变更,可以先尝试非阻塞 DDL,如果长时间无法完成,再启用抢占式 DDL 强制执行:
-- 第一次尝试:非阻塞模式,不影响业务
SET tdsql_ddl_block_mode = 'nonblock';
SET tdsql_ddl_nonblock_retry_times = 10;
ALTER TABLE orders ADD COLUMN remark VARCHAR(255);
-- 如果重试耗尽仍失败...

-- 第二次尝试:抢占模式,强制完成
SET tdsql_ddl_block_mode = 'preemptive';
SET tdsql_ddl_preempt_after_wait_seconds = 5;
ALTER TABLE orders ADD COLUMN remark VARCHAR(255);
总结:
1. 非阻塞 DDL 核心通过 tdsql_ddl_block_mode(普通线程)/ tdsql_ddl_recovery_block_mode(恢复线程)设置为 nonblock 启用,配套参数控制重试策略;
2. 非阻塞 DDL 采用「尝试 - 退让 - 重试」逻辑,避免阻塞业务事务,适合高并发、对可用性要求高的场景;
3. 可结合抢占式 DDL 使用,优先通过非阻塞模式保障业务,必要时切换抢占模式强制完成 DDL。

相关文档

帮助和支持

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

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

文档反馈