第一次接触Online DDL是在五年前的一个深夜,当时我们生产环境的用户表需要新增一个字段,执行ALTER TABLE后整个数据库连接池瞬间爆满,前端页面直接报错。那次事故让我深刻意识到传统DDL操作的致命缺陷——它会锁表阻塞所有读写请求。
Online DDL的出现彻底改变了这种局面。简单来说,它允许我们在不锁表或短暂锁表的情况下完成表结构变更。想象一下给行驶中的汽车更换轮胎,这就是Online DDL的神奇之处。MySQL从5.6版本开始引入这项技术,经过5.7的完善,到8.0已经发展出三种成熟的算法:COPY、INPLACE和INSTANT。
这三种算法就像装修房子的三种方案:COPY相当于另建新房再搬家(最稳妥但耗时),INPLACE类似原地翻修(平衡方案),INSTANT则是换个门牌号就完事(最快但限制多)。作为DBA,我们需要根据业务场景选择最合适的"装修方案"。
COPY算法的工作流程就像搬家:
sql复制-- 典型COPY算法操作示例
ALTER TABLE users ADD COLUMN age INT, ALGORITHM=COPY;
这个过程中最耗时的就是第二步数据拷贝。我做过测试,一张500万行的表添加字段,COPY算法耗时是INPLACE的3倍。而且需要双倍存储空间,这在磁盘紧张的服务器上简直是灾难。
但它的优势是兼容性最好,所有MySQL版本都支持,适合小型表或需要复杂变更的场景。去年我们给金融系统做字段类型变更(VARCHAR转BIGINT),就只有COPY算法能保证数据绝对安全。
INPLACE算法就像老房改造:
sql复制-- INPLACE算法添加索引
ALTER TABLE orders ADD INDEX idx_created_at(created_at), ALGORITHM=INPLACE;
这里有个关键点:INPLACE不一定等于Online!根据我的经验,大约30%的INPLACE操作仍会短暂锁表。比如删除主键这种操作,虽然用INPLACE但会阻塞写入。
性能方面,在MySQL 5.7上给亿级表加索引,INPLACE比COPY快60%。但要注意磁盘空间,虽然不需要双倍空间,但临时文件可能暴涨。有次我们给200GB的表加索引,临时文件一度涨到180GB,差点撑爆磁盘。
INSTANT算法是MySQL 8.0的杀手锏,它的原理就像改户口本:
sql复制-- INSTANT方式添加可空列
ALTER TABLE products ADD COLUMN discount DECIMAL(5,2) NULL, ALGORITHM=INSTANT;
但现实很骨感,目前INSTANT只支持有限操作:
我在测试环境做过对比:同样添加可空列,INSTANT耗时0.01秒,INPLACE要28秒(100万行数据)。可惜生产环境大多还在用5.7,这个神器暂时难以普及。
根据多年踩坑经验,我总结出这个决策框架:
| 维度 | COPY | INPLACE | INSTANT |
|---|---|---|---|
| 耗时 | 长(O(n)) | 中(O(n/2)) | 短(O(1)) |
| 空间占用 | 2倍表空间 | 1.2倍表空间 | 可忽略 |
| 锁阻塞 | 全程阻塞 | 部分阻塞 | 无阻塞 |
| 兼容性 | 全版本 | 5.6+ | 8.0.12+ |
具体选型时,我通常会问四个问题:
场景一:电商大促前加字段
sql复制ALTER TABLE users ADD COLUMN vip_level TINYINT DEFAULT 0,
ALGORITHM=INPLACE, LOCK=NONE;
场景二:凌晨维护窗口改主键
sql复制-- 提前通知停机维护
ALTER TABLE orders DROP PRIMARY KEY, ADD PRIMARY KEY(new_id),
ALGORITHM=COPY;
场景三:SaaS产品动态扩展
sql复制-- 需要确认版本支持
ALTER TABLE tenant_data ADD COLUMN dynamic_field VARCHAR(255) NULL,
ALGORITHM=INSTANT;
执行Online DDL时,我习惯开三个窗口:
SHOW PROCESSLIST看State字段SELECT * FROM sys.schema_table_lock_waits对于大型表,这些参数可以优化速度:
sql复制-- 调整排序缓冲区
SET SESSION sort_buffer_size = 64*1024*1024;
-- 增加并行线程
SET SESSION innodb_online_alter_log_max_size = 512*1024*1024;
错误1:"Lock wait timeout exceeded"
SHOW ENGINE INNODB STATUS查阻塞源错误2:"Duplicate entry for key"
错误3:"Disk is full"
当原生Online DDL不能满足时,我会考虑:
bash复制gh-ost \
--host="127.0.0.1" \
--database="test" \
--table="big_table" \
--alter="ADD COLUMN remark TEXT" \
--execute
bash复制pt-online-schema-change \
--alter "ADD COLUMN status TINYINT(1)" \
D=test,t=users \
--execute
工具对比:
MySQL 5.6到8.0的Online DDL进化令人惊艳:
有个有趣的发现:8.0.23之后,添加自增列也支持INPLACE了。之前这类操作必须用COPY算法,现在可以这样优雅处理:
sql复制ALTER TABLE logs ADD COLUMN id BIGINT AUTO_INCREMENT PRIMARY KEY,
ALGORITHM=INPLACE;
不过要注意版本差异,我有次在5.7执行8.0才支持的语法,直接导致复制中断。现在执行任何DDL前,都会先用SELECT @@version确认版本特性。