mysql在线DDL
Online DDL 详解
传统的 DDL(如 ALTER TABLE)操作会全程锁表(MySQL 5.5 及以前),在操作期间,表对于读写操作均处于不可用状态,对业务影响很大。
Online DDL 是从 MySQL 5.6 开始引入,并在 5.7 和 8.0 中不断增强的特性。它允许在 DDL 操作(如创建索引、添加列)的同时,执行并发 DML 操作(SELECT, INSERT, UPDATE, DELETE),极大减少了数据库的停机时间。
核心原理:Online DDL 在执行过程中,会在引擎层维护一个增量日志(Online Log),用于记录 DDL 期间发生的并发 DML 操作。当 DDL 完成后,再将增量日志中的应用到最终表中,从而保证数据一致性。
Online DDL 的语法与关键参数
使用 Online DDL 的关键是在 ALTER TABLE 语句中指定两个关键参数:ALGORITHM 和 LOCK。
1. ALGORITHM(算法)
指定执行 DDL 操作所使用的算法。
-
ALGORITHM=COPY
最传统的方式。MySQL 会创建一个与原表结构相同的临时表,执行 DDL 后,将原表数据逐行拷贝到临时表,最后通过重命名进行替换。
特点:需要锁写,阻塞所有 DML 操作;占用双倍磁盘空间;速度慢。
尽量避免使用。 -
ALGORITHM=INPLACE
“原地”操作。无需创建完整的临时表拷贝数据,但可能在 InnoDB 内部重建表(例如,重建聚簇索引)。
特点:多数情况下允许并发 DML(取决于 LOCK 选项);比 COPY 算法快,占用空间少。这是 Online DDL 的核心。 -
ALGORITHM=INSTANT(MySQL 8.0 新增)
“瞬间”操作。仅修改数据字典(元数据),而不触及表中的数据。操作通常在毫秒级完成。
特点:速度极快,完全不阻塞 DML。适用于 ADD COLUMN、DROP COLUMN(某些情况)等操作。 -
ALGORITHM=DEFAULT
让 MySQL 自动选择最合适的算法。
2. LOCK(锁策略)
指定执行 DDL 时所需的锁级别。你可以要求 MySQL 使用尽可能少的锁,如果要求无法满足,操作可能会失败。
-
LOCK=NONE
最高并发级别。允许所有并发 DML 操作。这是在线操作的目标。
如果 MySQL 判断无法在不锁表的情况下完成操作,语句会执行失败。 -
LOCK=SHARED
允许并发读取(SELECT),但阻塞写入(INSERT, UPDATE, DELETE)。
使用时需要谨慎,仍然会影响业务。 -
LOCK=EXCLUSIVE
排它锁,等同于传统 DDL。会阻塞所有并发 DML 操作。
除非万不得已,否则不要使用。 -
LOCK=DEFAULT
让 MySQL 自动选择最强的并发锁策略(NONE -> SHARED -> EXCLUSIVE)。
如何使用:最佳实践与示例
最佳实践:始终显式指定 ALGORITHM 和 LOCK,不要让 MySQL 自行决定,以避免意外情况。
1. 在线创建索引(最经典的场景)
-- 强烈推荐写法:明确要求使用INPLACE算法和不锁表
ALTER TABLE `your_large_table`
ADD INDEX `idx_email` (`email_column`),
ALGORITHM=INPLACE,
LOCK=NONE;
说明:在 MySQL 5.6+ 中,添加二级索引操作完全支持 INPLACE 和 NONE,是真正的在线操作,对业务影响极小。
- 在线添加字段
情况一:添加可为 NULL 的字段或带常量默认值的字段(MySQL 8.0+)
-- MySQL 5.7 及以后:通常使用 INPLACE
ALTER TABLE `your_large_table`
ADD COLUMN `new_status` VARCHAR(10) NOT NULL DEFAULT 'active',
ALGORITHM=INPLACE, -- 在MySQL 8.0+中,如果支持,会使用INSTANT
LOCK=NONE;
-- MySQL 8.0+:如果支持,优先使用INSTANT,速度极快
ALTER TABLE `your_large_table`
ADD COLUMN `new_flag` TINYINT(1) DEFAULT 0,
ALGORITHM=INSTANT,
LOCK=NONE;
情况二:添加一个不允许为NULL且无默认值的字段
这个操作在 MySQL 5.7 中通常需要重建表(ALGORITHM=COPY),因为必须为已有数据填充值。
应避免直接操作,或使用 pt-online-schema-change 等工具。
3. 在线删除索引
ALTER TABLE `your_large_table`
DROP INDEX `idx_old_column`,
ALGORITHM=INPLACE, -- 删除索引是非常快的元数据操作,通常使用INSTANT
LOCK=NONE;
- 先检查再执行
在执行不确定的操作前,可以先使用 NO_WAIT 或 WAIT 选项进行测试,如果 MySQL 无法满足你的锁要求,它会直接报错而不是开始执行。
-- 测试这个操作是否能以“不锁表”的方式完成
ALTER TABLE `your_large_table`
ADD INDEX `idx_test` (`some_column`),
ALGORITHM=INPLACE,
LOCK=NONE,
WAIT=5; -- 设置超时时间为5秒,如果5秒内无法获取所需锁则退出
-- 或者更严格的测试
ALTER TABLE ... ALGORITHM=INPLACE, LOCK=NONE, NO_WAIT;
如果上述语句执行成功,说明语法和锁条件都满足。如果报错 ERROR 1205 (HY000): Lock wait timeout exceeded 或类似信息,则说明当前无法以不锁表的方式完成操作,需要另寻方案(如使用工具或在更低峰期重试)。
注意事项与限制
并非完全无锁
即使是 ALGORITHM=INPLACE 和 LOCK=NONE,在 DDL 操作的开始和结束阶段仍然需要短暂的排他元数据锁(MDL)来更新表结构。这个时间极短,通常感知不到,但在有非常长事务或未提交事务时可能会被阻塞。
性能影响
Online DDL 操作期间,数据库的 I/O 和 CPU 负载会显著增加(因为要维护 Online Log 和重建数据)。建议在业务低峰期执行。
磁盘空间
ALGORITHM=INPLACE 操作通常需要额外的磁盘空间(最多相当于表大小的大小)。ALGORITHM=COPY 则需要两倍空间。
并非所有 DDL 都支持 Online
支持:ADD INDEX/DROP INDEX、ADD COLUMN(部分情况)、DROP COLUMN(部分情况)、RENAME COLUMN(8.0+)等。
不支持:修改列的数据类型(如 VARCHAR(20) 改为 VARCHAR(30))、更改表字符集、删除主键、优化表(OPTIMIZE TABLE)等。这些操作通常需要 ALGORITHM=COPY。
版本差异
MySQL 8.0 的 Online DDL 能力远强于 5.7,特别是 ALGORITHM=INSTANT 的引入。在执行前,务必查阅对应版本的官方文档,确认你想要的操作是否支持以及支持的程度。
总结
| 操作场景 | 推荐写法 | 说明 |
|---|---|---|
| 加索引 | ALTER TABLE … ADD INDEX …, | ✅ 5.6+ 首选 ❗️影响小 |
| 删索引 | ALTER TABLE … DROP INDEX …, | ⚡️ 速度快 📌元数据操作 |
| 加字段 | ALTER TABLE … ADD COLUMN …, | ️ 5.7 常用 |
| 加字段 (8.0+) | ALTER TABLE … ADD COLUMN …, | 首选 ⏱️瞬间完成 |
最终建议
对于任何线上大表的 DDL 操作,遵循以下流程:
备份:操作前先备份数据。
检查:在测试环境验证 DDL 语句,或使用 NO_WAIT 测试。
选择工具:优先使用 Native Online DDL(ALGORITHM=INPLACE/INSTANT, LOCK=NONE),如果不支持,则选用 pt-online-schema-change 或 gh-ost。
低峰操作:在业务低峰期执行。
监控:执行期间监控数据库性能和空间使用情况。
