pt-online-schema-change通过影子表、触发器同步和原子替换实现无锁在线ddl,全程不影响业务读写,是mysql大表结构变更的成熟解决方案。

不能靠 cron + ALTER TABLE 自动跑——它会锁表、拖垮主从、且不区分表是否真该转;真正能落地的自动化,必须分三步:先精准识别“该转且能转”的表,再用 pt-online-schema-change 安全执行,最后自动验证关键指标。
怎么筛出真正需要转换的 MyISAM 表
直接查 information_schema.tables 会拉出一堆只读小表或带 FULLTEXT 的遗留表,它们不该进自动化流水线。重点只盯三类:有写入、数据量 >1GB、需事务或行锁。
- 执行这句获取候选清单:
SELECT table_schema, table_name, table_rows, data_length FROM information_schema.tables WHERE engine = 'MyISAM' AND table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys') AND data_length > 1073741824 AND table_rows > 0; -
table_rows = 0或NULL的表别信——MyISAM 统计不准,得结合慢查日志或监控确认是否有 INSERT/UPDATE 流量 - 排除含
FULLTEXT索引的表(SHOW CREATE TABLE t检查),MySQL ERROR 1214 - 检查是否有无主键/唯一索引的表:
SELECT table_schema, table_name FROM information_schema.tables t LEFT JOIN (SELECT table_schema, table_name FROM information_schema.statistics WHERE non_unique = 0 AND seq_in_index = 1 GROUP BY table_schema, table_name) pk USING (table_schema, table_name) WHERE t.engine = 'MyISAM' AND pk.table_name IS NULL;——这类表pt-online-schema-change直接拒绝处理
为什么不能用 ALTER TABLE ENGINE=InnoDB 写进脚本
它在 MySQL 8.0+ 仍走 COPY 算法,ALGORITHM=INPLACE 对引擎切换无效;所谓“在线”只是最后几秒元数据切换,中间全程拷贝数据,大表一跑就是几十分钟。
- 主库执行时持 MDL 写锁,所有对该表的读写都卡住,应用端出现大量
Lock wait timeout exceeded - 从库回放 binlog 也得等锁,
Seconds_Behind_Master瞬间飙到几千秒 - 磁盘空间需求翻倍:原表 5GB,转换过程临时占用至少 5GB 新空间,
df -h不够就直接 OOM - 即使加了
LOCK=NONE,MySQL 也会静默降级为LOCK=SHARED,写入照样被阻塞
如何用 pt-online-schema-change 实现安全批量转换
它通过影子表 + 触发器双写绕过锁表,但默认行为不适合生产环境直跑,必须调参压控节奏。
- 基础命令必须带这些参数:
pt-online-schema-change --alter "ENGINE=InnoDB" D=your_db,t=table_name --execute --chunk-size=1000 --max-lag=1 --check-interval=5 --chunk-time=0.5 -
--chunk-size=1000控制每批迁移行数,避免单次操作太久触发复制延迟;太大(如默认 1000)可能让从库积压 -
--max-lag=1是安全阀:从库延迟超 1 秒自动暂停,防止雪崩;不设这个等于裸奔 - 必须确保目标表有主键或唯一非空索引,否则报错
This table has no primary key or unique index,且无法修复 - 执行完不会删旧文件(
.MYD/.MYI),得人工确认新表COUNT(*)一致后再删,否则磁盘空间永远涨不回去
转换后必须自动验证的三项硬指标
引擎改完不验证,等于没转——InnoDB 和 MyISAM 在 COUNT、全文检索、自增值行为上完全不同,业务 SQL 可能悄无声息地变慢或失败。
- 对比
COUNT(*):SELECT COUNT(*) FROM old_table;和SELECT COUNT(*) FROM new_table;必须严格相等,否则说明触发器漏写或双写异常 - 检查全文查询是否还能跑:
SELECT * FROM t WHERE MATCH(col) AGAINST('+apple -orange' IN BOOLEAN MODE);——InnoDB 对嵌套括号和布尔语法支持更弱,MyISAM 能跑的语句在这儿可能返回空 - 验证事务行为:显式开启事务
BEGIN; UPDATE t SET x=1 WHERE id=1; ROLLBACK;,再查原值是否恢复;MyISAM 下这条 UPDATE 会直接生效,InnoDB 下必须看到回滚效果才算成功 - 别忘了调配置:
innodb_buffer_pool_size拉到物理内存 50%–75%,key_buffer_size降到 32M 左右,否则 MyISAM 缓存还在吃内存,InnoDB 却饿着
最易被忽略的是触发器和临时表隐性依赖:应用里有没有 CREATE TEMPORARY TABLE tmp AS SELECT ...?默认引擎是 MyISAM,不显式加 ENGINE=InnoDB,半夜跑批处理就会失败;还有硬编码的 INSERT DELAYED,InnoDB 根本不支持,得全代码库 grep 清理。











