← 返回文章

大表DDL

发布于 2026/10/17

1. 完整回答

大表 DDL 指的是对数据量较大的数据库表执行结构变更,比如新增字段、修改字段类型、添加索引或者调整约束。主要需要解决的问题是:在业务持续读写的情况下,尽量降低表结构变更对数据库性能和可用性的影响。

以 MySQL InnoDB 为例,原生 DDL 主要有三种执行算法:COPY、INPLACE 和 INSTANT。

COPY 会复制原表数据,执行期间通常会阻塞写入;INPLACE 可以避免传统的整表复制,部分操作允许并发读写,但仍然可能扫描或重建整张表;INSTANT 只修改元数据,不需要遍历原表数据,执行速度通常很快。

需要注意,Online DDL 也可能发生阻塞。因为 DDL 在执行过程中需要获取元数据锁,也就是 MDL 锁。如果存在长事务,DDL 可能一直等待锁,后续业务请求也可能排队,最终造成数据库连接堆积。

对于 MySQL 原生 DDL 无法低影响完成的操作,可以使用 pt-online-schema-change 或 gh-ost。它们通过创建新表、分批复制历史数据、同步增量变更,最后切换新旧表,降低长时间阻塞业务的风险。

实际生产中,一般会先确认 MySQL 版本和 DDL 支持的算法。如果支持 INSTANT,优先使用;如果需要重建大表,就评估原生 Online DDL 或在线变更工具,同时控制数据库负载、主从延迟和切换阶段的锁等待。

2. 核心原理与关键概念

2.1 MySQL 原生 DDL 的执行机制

MySQL InnoDB 主要提供三种 DDL 算法。

算法 核心机制 是否扫描或重建大量数据 对业务的影响
INSTANT 修改数据字典中的元数据 通常不需要 影响最小,但仍可能等待 MDL
INPLACE 在存储引擎内部执行变更 取决于具体操作,可能重建整表 部分操作允许并发读写,资源消耗可能较大
COPY 创建新表结构并复制原表数据 需要复制数据 通常允许并发查询,但会阻塞写入

核心区别在于是否需要处理原有数据,以及执行期间允许多大程度的并发访问。

例如:

  • 新增普通字段:符合条件时可以使用 INSTANT。

  • 新增普通二级索引:通常使用 INPLACE,需要扫描数据并构建索引。

  • 修改字段数据类型:通常需要 COPY,可能长时间阻塞写入。

具体支持情况取决于 MySQL 版本、存储引擎、表结构和变更类型。

2.2 Online DDL 的核心流程

以支持并发 DML 的 InnoDB 在线创建二级索引为例:

初始化 → 构建索引 → 记录并应用增量变更 → 提交表结构

  1. 初始化:检查执行算法和并发能力,获取必要的 MDL,保护当前表结构。

  2. 构建索引:扫描原表数据,排序并构建新的二级索引,同时允许业务继续读写。

  3. 增量同步:通过在线变更日志记录执行期间的相关 DML,并在完成前应用这些变化,保证索引数据一致。

  4. 提交变更:获取短暂的排他 MDL,完成表结构切换,使新索引正式生效。

这里最重要的两个问题是:

  • 性能问题:扫描数据、构建索引和处理增量变更会消耗 CPU、I/O 和磁盘空间。

  • 锁等待问题:初始化和提交阶段可能需要排他 MDL,长事务可能使短暂的锁操作变成长时间等待。

2.3 在线变更工具的核心流程

对于需要重建大表的 DDL,pt-online-schema-change 和 gh-ost 使用相似的迁移思路:

创建影子表 → 修改新表结构 → 分批复制数据 → 同步增量 → 切换新旧表

两种工具的核心区别是增量同步方式:

  • pt-online-schema-change(pt-osc):通过触发器同步 INSERT、UPDATE、DELETE。

  • gh-ost:通过解析 Row Based Binary Log(行格式二进制日志)获取增量变更,再异步应用到影子表。

两种方案都需要处理数据一致性、复制进度、主从延迟以及最终切换时的 MDL 问题。

2.4 关键概念

概念 含义
DDL 数据定义语言,用于创建、修改和删除数据库对象
Online DDL 尽量允许业务并发访问的表结构变更
MDL 元数据锁,保护表结构与并发访问之间的一致性
DML INSERT、UPDATE、DELETE 等数据操作
影子表(Ghost Table) 按目标结构创建的新表,用于迁移原表数据
Cut-over 增量同步完成后,将新表切换为正式业务表的过程

3. 高频面试追问

3.1 为什么大表 DDL 容易造成线上故障?

主要有三个原因:锁等待、资源消耗和复制延迟。

首先,DDL 需要 MDL。如果长事务一直持有表的元数据锁,DDL 申请排他锁时就可能被阻塞,后续查询也可能排队,造成连接数快速增加。

其次,对于重建表或者创建索引的操作,需要扫描大量数据,消耗 CPU、磁盘 I/O 和临时空间,可能影响正常业务查询。

最后,在主从复制架构中,大型 DDL 可能导致从库复制长时间无法推进,产生明显的主从延迟。

所以大表 DDL 的主要风险既包括锁表,也包括执行期间的持续资源压力。

3.2 Online DDL 为什么还可能阻塞读写?

因为 Online DDL 仍然需要 MDL,并发 DML 能否执行和 MDL 能否及时获取是两个不同的问题。

MySQL 在提交新表结构时,需要获取排他 MDL。如果此时存在尚未提交的长事务,DDL 就需要等待。

例如:

事务 A 查询表后未提交 → DDL 申请排他 MDL 并等待 → 后续事务 B 查询同一张表也可能被阻塞

这种情况下,即使 DDL 本身允许并发 DML,也可能导致业务请求堆积。

因此,执行 DDL 前需要排查长事务,执行时要监控 MDL 等待,并设置合理的锁等待超时。

继续追问:如果线上已经出现大量 Waiting for table metadata lock,怎么处理?

首先通过 SHOW FULL PROCESSLIST 和 performance_schema.metadata_locks 确认阻塞关系。

如果发现 DDL 正在等待长事务,优先评估取消等待中的 DDL,让业务恢复正常,再处理长事务并重新安排变更。

处理时需要确认会话身份和事务影响,避免直接终止重要业务事务。

3.3 INPLACE 一定不会重建表吗?

不一定。INPLACE 并不代表完全不处理原表数据。

INPLACE 表示避免使用传统 COPY 算法进行整表复制,但部分操作仍然需要重建表的数据结构。

例如:

  • 添加普通二级索引:通常不需要重建聚簇索引,但需要扫描数据并构建新索引。

  • 修改字段是否允许 NULL:通常需要重建表。

  • 调整字段位置:通常需要重建表。

因此,判断 DDL 成本时,除了看执行算法,还要确认是否需要重建表、是否允许并发 DML。

3.4 如何给一张上亿行的订单表添加索引?

首先确认索引是否必要,检查查询条件、现有索引和执行计划,避免创建重复或收益有限的索引。

如果是 MySQL InnoDB 支持在线创建的普通二级索引,可以采用:

ALTER TABLE orders
ADD INDEX idx_user_id (user_id),
ALGORITHM=INPLACE,
LOCK=NONE;

这里通过 ALGORITHM=INPLACE 避免退化为 COPY,通过 LOCK=NONE 要求支持并发读写。如果无法满足这些条件,语句会报错。

不过,上亿行数据仍然需要扫描并构建索引,所以要提前评估磁盘空间、I/O、执行时间和主从延迟。

如果业务负载较高、无法承受原生 DDL 的资源压力,可以进一步评估 gh-ost 等可限速的在线变更方案。

3.5 pt-online-schema-change 和 gh-ost 有什么区别?

核心区别是增量数据的同步方式。

pt-osc 在原表创建触发器,业务发生写入时同步维护新表,所以增量同步比较直接,但会增加原表写入开销。

gh-ost 通过 Binlog 捕获增量,再异步写入新表,避免了触发器带来的同步写入压力,并且能够更灵活地暂停、限速和控制切换时间。

两者都需要复制原有数据,也都需要在最终切换阶段处理元数据锁。

选择时还需要检查限制:gh-ost 通常要求存在可用于数据遍历和匹配的主键或唯一键,并且不支持外键和触发器相关的表结构;pt-osc 对触发器和外键也有相应限制,需要提前验证。

3.6 线上执行大表 DDL,一般怎么设计完整流程?

一般会按 变更评估 → 预演 → 正式执行 → 验证 → 收尾 来处理。

首先确认变更类型、MySQL 版本、表大小、索引、外键,以及是否存在长事务,判断能否使用 INSTANT 或在线变更。

接着在测试环境或从库进行预演,评估执行时间、磁盘空间和性能影响,明确失败处理方案。

正式执行时选择业务低峰期,设置合理的锁等待超时,监控数据库负载、主从延迟、DDL 进度和业务错误率。如果采用 gh-ost,可以通过限速控制迁移压力,并安排合适的 Cut-over 时间。

完成后检查表结构、索引状态和数据一致性,同时观察业务 SQL 是否正常。

如果涉及字段删除或者不兼容的字段修改,还会提前完成应用兼容性改造,避免新旧版本应用在发布期间发生冲突。

3.7 大表新增字段、修改字段类型和添加索引,处理方式一样吗?

不一样,关键取决于变更是否需要处理已有数据。

新增普通字段,在支持 INSTANT 的版本及表结构条件下,只需要调整元数据,通常可以快速完成。

添加二级索引,需要读取已有数据并构建索引,虽然可以支持并发读写,但执行时间会随数据规模增长。

修改字段数据类型通常涉及存储格式转换,需要复制或重建数据,风险更高。

例如,将订单表的 INT 字段修改为 BIGINT,通常需要 COPY 算法。对于上亿行数据,需要考虑在线迁移工具,或者通过新增字段、分批回填、应用兼容和后续清理进行分阶段迁移。

继续追问:MySQL 8.0 的 INSTANT 新增字段有什么版本区别?

MySQL 8.0.12 开始支持 INSTANT 新增字段,但最初只能在表末尾添加;8.0.29 开始支持在任意位置快速加列,并支持快速删除列。

INSTANT 也存在行版本数量、表结构类型等限制。执行前需要结合实际版本确认,不能认为所有新增和删除字段操作都能瞬间完成。

3.8 大表 DDL 执行期间需要监控什么?

主要关注四个方面。

第一是 MDL 等待,检查是否存在长事务、DDL 等待和业务请求排队。

第二是 数据库负载,包括 CPU、磁盘 I/O、活跃连接数、查询延迟和错误率。

第三是 磁盘与变更日志空间。原生 Online DDL 可能产生临时排序文件和在线变更日志,日志超过 innodb_online_alter_log_max_size 限制时,DDL 可能失败。

第四是 主从复制延迟。原生 DDL 在主库执行完成后才会在从库执行对应变更,可能导致从库复制停滞;在线迁移工具的数据复制也可能增加复制压力。

如果使用 gh-ost 或 pt-osc,还需要监控数据复制进度、增量追赶情况和限速状态。

3.9 为什么大表 DDL 通常需要主键或者唯一键?

主要是为了高效定位数据、分批复制以及正确同步增量变化。

例如,在线迁移工具复制订单表时,可以根据主键范围分批读取,降低单次操作的数据量。

当某条记录发生 UPDATE 或 DELETE 时,也可以利用主键或者稳定的唯一键找到新表中的对应记录。

缺少合适的唯一标识,会增加数据匹配、增量同步和一致性保障的难度。

因此,gh-ost 要求变更前后的表具有可共同使用的主键或者唯一键;pt-osc 在多数情况下也要求存在主键或唯一索引。

3.10 DDL 执行失败后可以直接回滚吗?

需要区分 DDL 执行失败恢复和业务变更回退。

MySQL 8.0 的 InnoDB 支持原子 DDL,能够保证受支持的 DDL 在执行失败或数据库崩溃时,不会留下部分提交的表结构变更。

但原子 DDL 不代表可以像普通事务一样执行 ROLLBACK 撤销已经成功提交的结构变更。

如果 DDL 已经成功执行,想恢复旧结构通常需要再次执行反向 DDL,或者使用提前准备的迁移回退方案。

对于影子表迁移,部分工具可以在切换后保留旧表,便于核查和制定回退方案。但如果切换后新表继续产生写入,直接切回旧表可能丢失新数据,所以仍需考虑增量数据处理和应用兼容性。

4. 易混淆概念对比

4.1 INSTANT、INPLACE、COPY

对比维度 INSTANT INPLACE COPY
修改方式 更新元数据 存储引擎内部完成变更 复制数据到新表
数据处理成本 通常很低 低到很高,取决于操作 通常较高
是否可能重建整表 否 是 复制整表数据
并发 DML 支持,但可能等待 MDL 部分操作支持 通常不支持
适合场景 支持快速变更的列操作 创建二级索引等 必须转换数据格式的操作

4.2 ALGORITHM 与 LOCK

参数 控制内容 示例
ALGORITHM DDL 采用什么算法执行 INPLACE、COPY、INSTANT
LOCK DDL 执行期间允许什么程度的并发访问 NONE、SHARED、EXCLUSIVE、DEFAULT

ALGORITHM=INPLACE, LOCK=NONE 表示要求使用 INPLACE 算法,并且允许并发读写。它仍然需要处理 MDL。

另外,ALGORITHM=INSTANT 只允许搭配 LOCK=DEFAULT,不能显式指定 LOCK=NONE。

4.3 原生 Online DDL、pt-osc、gh-ost

维度 原生 Online DDL pt-osc gh-ost
实现方式 MySQL 存储引擎处理 影子表 + 触发器 影子表 + Binlog
增量同步 InnoDB 内部维护 触发器同步 Binlog 异步应用
负载控制 缺少直接的暂停和限速机制 可控制分批复制速度 支持更灵活的暂停和限速
额外写入压力 取决于变更操作 触发器增加同步写入 异步维护影子表
主要限制 算法和并发能力受变更类型限制 受触发器、外键等条件限制 依赖 Binlog 和唯一键,不支持外键及触发器
适用场景 原生支持的低成本变更 需要在线复制迁移的场景 需要灵活控制迁移负载的场景

4.4 MDL 锁与行锁

维度 MDL 锁 行锁
保护对象 表结构及相关元数据 表中的数据记录或索引记录
主要作用 协调 DDL 与并发访问 协调事务之间的数据读写
常见阻塞 DDL 等待长事务,后续查询排队 多个事务更新相同记录
排查方式 metadata_locks InnoDB 事务及数据锁信息

MDL 的特点是影响范围可能扩展到整张表,因此即使 DDL 只需要短暂的排他锁,也可能因等待而引发大面积业务阻塞。

4.5 Online DDL 与原子 DDL

概念 解决的问题 核心特点
Online DDL 变更期间的业务可用性 尽量允许并发访问
原子 DDL 变更失败时的结构一致性 成功整体提交,失败整体撤销
事务性 DDL 在用户事务中控制结构变更提交与回滚 MySQL 原子 DDL 不提供这种能力

5. 总结

大表 DDL 的核心问题,是在修改表结构的同时控制业务阻塞、数据库负载和数据一致性风险。MySQL InnoDB 提供 INSTANT、INPLACE 和 COPY 三种算法,能够只修改元数据时优先使用 INSTANT,需要扫描或重建数据时再评估 Online DDL 的执行成本。如果原生变更风险较高,可以使用 pt-osc 或 gh-ost,通过影子表、分批复制和增量同步完成迁移。需要特别关注的是,Online DDL 不代表完全无锁,长事务可能导致 MDL 等待,进而阻塞业务请求。因此,生产环境的大表 DDL 需要提前评估执行算法,做好预演、限速、监控和回退设计。