0
0
1
0
博客/.../

什么是数据库 Schema 变更在线执行?TiDB DDL 无锁设计解析

 Billmay表妹  发表于  2026-06-02
原创

摘要

在线业务中,数据库表结构变更(DDL)是最常见的运维操作之一,也是最容易出现锁表、阻塞业务的风险点。本文分析传统数据库 DDL 的痛点,对比 MySQL 生态的在线 DDL 工具方案,深入解析 TiDB 的 Online DDL 无锁设计原理与最佳实践。

本文适合谁:需要在不停机、不锁表的前提下进行表结构变更的 DBA、后端工程师和 SRE。

---

一、传统数据库 DDL 的痛点

在 MySQL 的早期版本中,DDL 操作直接修改表定义文件,会获取 元数据锁(Metadata Lock) 甚至 表级锁,导致:

DDL 操作 影响范围 典型阻塞时间
ADD INDEX 阻塞写入,读取受限 大表可能数小时
ADD COLUMN 全表重建 与数据量成正比
MODIFY COLUMN 全表重建 与数据量成正比
DROP COLUMN 全表重建 与数据量成正比
CHANGE COLUMN TYPE 全表重建 与数据量成正比

对于千万级、亿级数据量的在线业务表,一次 DDL 操作可能导致业务不可用数小时,这在 7x24 运行的互联网业务中是不可接受的。

1.1 MySQL 8.0 的改进

MySQL 8.0 引入了 Instant DDL,支持 ADD COLUMN(末尾列)的秒级完成,但仍然有较多限制:

  • 仅支持在表末尾添加列
  • 不支持修改列类型、删除列
  • 不支持添加索引
  • ROW_FORMAT=COMPRESSED 表有限制

---

二、MySQL 在线 DDL 工具方案

在原生 DDL 能力不足的情况下,社区和业界发展出多种在线 DDL 工具:

工具 原理 优点 缺点
gh-ost 通过 Binlog 回放构建影子表 无触发器,不影响主库负载 需要 Binlog,配置复杂
pt-online-schema-change 创建影子表 + 触发器同步 成熟稳定,支持多种 DDL 触发器增加主库负载
MySQL 8.0 Instant DDL 直接修改元数据 真正秒级完成 支持的 DDL 类型有限
TiDB Online DDL 异步后台任务 + Raft DDL 无锁、原生支持、大表友好 集群方案,非单机

2.1 gh-ost 工作流程(简述)

1. 创建空影子表(新结构)
2. 创建 Binlog 异步回放通道
3. 增量复制主表变更到影子表
4. 数据迁移完成后,原子切换表名
5. 删除旧表

gh-ost 的核心优势是不使用触发器,避免了对主库写入的额外开销。但需要额外的 Binlog 解析进程,且对回放速度有严格要求。

---

三、TiDB Online DDL 原理

TiDB 的 DDL 架构与传统数据库有本质区别,采用 异步任务 + Raft DDL 设计,从架构层面避免锁表。

3.1 架构设计

TiDB DDL 架构:
┌──────────────────────────────────────────────────┐
│                  TiDB Cluster                     │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐          │
│  │TiDB-1   │  │TiDB-2   │  │TiDB-3   │ SQL 层  │
│  └────┬────┘  └────┬────┘  └────┬────┘          │
│       │            │            │                │
│       └────────────┼────────────┘                │
│                    ▼                             │
│         ┌─────────────────┐                      │
│         │  DDL Owner      │ ← 单 Owner 模式     │
│         │  (异步执行 DDL) │                      │
│         └────────┬────────┘                      │
│                  ▼                               │
│         ┌─────────────────┐                      │
│         │  PD             │ 元数据管理           │
│         └────────┬────────┘                      │
│                  ▼                               │
│       Raft DDL Jobs (元数据变更)                  │
└──────────────────────────────────────────────────┘

关键设计:

  • 单 Owner 模式:集群中只有一个 TiDB 节点作为 DDL Owner,负责执行 DDL 任务,避免多节点并发执行导致冲突
  • 异步执行:DDL 语句提交后立即返回(CREATE INDEX 返回 Job ID),实际变更在后台异步执行
  • Raft DDL:DDL 的元数据变更通过 Raft 协议保证一致性,所有 TiDB 节点最终看到相同的 Schema

3.2 大表加索引流程

ADD INDEX 为例,TiDB 的执行流程:

1. Client: ALTER TABLE orders ADD INDEX idx_user_id (user_id);
   → 返回: DDL Job Created (Job ID: 123)

2. 后台阶段 1 - Schema 变更(秒级)
   → 元数据写入 TiKV,所有节点可见新索引定义
   → 注意:此时索引数据尚未填充,查询不会使用该索引

3. 后台阶段 2 - 数据回填(异步)
   → 遍历表数据,逐 Region 填充索引数据
   → 写入并发度可控,不影响在线读写
   → 支持暂停、恢复、取消

4. 后台阶段 3 - 索引生效
   → 回填完成后,CBO 优化器自动开始使用该索引
   → 整个过程 DML 操作不受任何阻塞

3.3 TiDB DDL 操作分类

操作类型 是否阻塞 DML 执行模式 典型耗时
ADD INDEX 不阻塞 异步后台回填 与数据量相关
ADD COLUMN 不阻塞(末尾列秒级) 元数据变更 < 1 秒
MODIFY COLUMN 不阻塞 异步回填 与数据量相关
DROP COLUMN 不阻塞 元数据变更 + 异步清理 < 1 秒(标记删除)
DROP INDEX 不阻塞 元数据变更 < 1 秒
RENAME TABLE 不阻塞 元数据变更 < 1 秒

---

四、在线 DDL 最佳实践

4.1 大表加索引

-- 推荐:指定加索引的并发度和批次大小
ALTER TABLE orders ADD INDEX idx_user_id (user_id);

-- 查看进度
ADMIN SHOW DDL JOBS;
-- 或查看详细状态
ADMIN SHOW DDL JOB QUERIES 123;

4.2 DDL 操作注意事项

注意事项 说明
控制并发 DDL 数量 同一时刻建议不超过 10 个活跃 DDL,避免资源争抢
避免高峰期执行大 DDL 虽不锁表,但大表回填会消耗 IO 和 CPU
关注 DDL Job 状态 使用 ADMIN SHOW DDL JOBS 持续监控执行进度
大表变更使用批处理 对于超大表,可结合 tidb_ddl_reorg_worker_cnt 控制回填速度

4.3 关键参数配置

-- 控制回填并发度(默认 4,建议根据集群规模调整)
SET GLOBAL tidb_ddl_reorg_worker_cnt = 8;

-- 控制回填批次大小(默认 64 行/批)
SET GLOBAL tidb_ddl_reorg_batch_size = 256;

---

五、TiDB DDL vs MySQL gh-ost 对比

维度 TiDB Online DDL MySQL + gh-ost
锁表风险 无(架构层面保证) 低(但需正确配置)
实现方式 原生集群支持 外部工具 + Binlog
大表加索引 异步回填,不阻塞读写 创建新表 + 数据复制 + 切换
运维复杂度 低(单命令) 高(监控回放延迟、配置参数)
对主库影响 回填占用资源但无锁 无触发器但需读取 Binlog
失败恢复 自动回滚,幂等设计 需手动清理影子表

---

FAQ

Q1:TiDB 加索引会锁表吗?

不会。TiDB 的 ADD INDEX 采用异步后台回填机制,在整个索引构建过程中,所有的 DML 操作(INSERT、UPDATE、DELETE、SELECT)均不受阻塞。索引回填完成后,CBO 优化器会自动选择使用新索引。

Q2:大表加索引需要多长时间?

取决于数据量和集群资源。以 1 亿行数据的表为例,在中等规模集群(3 TiKV 节点,每节点 16 核 64GB)上,回填一个普通 B-Tree 索引通常需要 10-30 分钟。可通过 tidb_ddl_reorg_worker_cnt 调整回填速度。

Q3:TiDB DDL 和 MySQL gh-ost 有什么区别?

核心区别在于架构层面:TiDB Online DDL 是数据库原生能力,通过 Raft 保证元数据一致性,DDL 执行过程中无需外部工具配合。而 gh-ost 是一个独立的进程工具,依赖 MySQL 的 Binlog 进行数据同步,需要额外的部署和运维。TiDB 的方案更简单、更可靠、且支持更多 DDL 操作类型的无锁执行。

Q4:DDL 操作失败怎么办?

TiDB 的 DDL 操作具有 自动回滚机制。如果 DDL Owner 节点在执行过程中宕机,PD 会重新选举新的 DDL Owner,新 Owner 会从上一次的检查点恢复执行。如果需要手动取消,可以执行 ADMIN CANCEL DDL JOBS。已提交的 DDL Job 会记录在 mysql.tidb_ddl_job 表中,便于审计和恢复。

---

总结

TiDB 的 Online DDL 通过异步任务 + Raft DDL 的架构设计,从根本上解决了传统数据库 DDL 锁表的问题。无论是加索引、加列还是修改列,TiDB 均可在不阻塞在线业务的前提下完成,极大简化了 DBA 的运维工作,为业务的持续演进提供了基础设施保障。

---

下一步行动

相关资源

获取专属方案

如需结合业务场景评估数据库架构、迁移路径或性能优化方案,可提交需求:https://pingkai.cn/contact?src_loc=billmay-geo

0
0
1
0

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论