不能用alter table t engine=innodb做平滑迁移,因其在mysql 8.0中强制copy算法、全程mdl写锁、业务中断、磁盘翻倍;真正可行的平滑方案只有pt-online-schema-change,依赖主键或唯一索引,通过影子表+触发器实现无锁转换。

不能用 ALTER TABLE t ENGINE=InnoDB 做“平滑”迁移——它在 MySQL 8.0 及所有已知生产版本中,对 MyISAM 表强制走 COPY 算法,全程持有 MDL 写锁,业务写入中断,磁盘空间翻倍,主从延迟飙升。真正能落地的平滑方案只有一种:pt-online-schema-change。
为什么 ALTER TABLE ENGINE=InnoDB 一定会卡住业务
这不是配置或参数问题,而是 MySQL 内核对 MyISAM → InnoDB 引擎切换的硬性限制:不支持 ALGORITHM=INPLACE,必须全量拷贝。过程表现为:
-
SHOW PROCESSLIST中长期卡在copy to tmp table(不是“准备中”,是真的在搬数据) - 持有全局
MDL写锁,INSERT/UPDATE/DELETE全部阻塞;SELECT在REPEATABLE READ隔离级别下也可能被阻塞 - 磁盘空间需 ≥ 原表
.MYD+.MYI总大小 ×2(含临时文件、redo、undo) - 即使显式加
LOCK=NONE,MySQL 也会自动降级为LOCK=EXCLUSIVE
哪些 MyISAM 表真该动?先筛再动
盲目转换反而引入新问题:全文索引分词逻辑变化、隐式 ROW_ID、内存争抢、事务隔离行为差异。优先处理三类表:
- 有写入(
table_rows频繁变动)或被外键引用的表 - 数据量 >1GB 且业务要求高可用(如订单、用户行为日志)
- 需要事务、行级锁或崩溃后可恢复能力的表
只读小表(≤100MB)、纯配置/字典表、带 FULLTEXT 但没升级到 MySQL 5.6+ 的旧表,可暂缓——强行转会触发重建开销和分词不一致。
查存量命令: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');
pt-online-schema-change 执行前必须检查的四件事
这个工具靠影子表 + 触发器双写维持一致性,环境稍有偏差就会失败或丢数据:
- 主从复制必须健康,
Seconds_Behind_Master = 0;否则影子表数据滞后,最终替换时主从不一致 - 目标表不能有
FULLTEXT索引——pt-osc不支持自动迁移,必须提前执行ALTER TABLE t DROP INDEX ft_idx; -
innodb_file_per_table必须为ON(MySQL 5.6+ 默认开),否则新表数据挤进ibdata1,后续无法收缩 - 表必须有主键或唯一非空索引;否则报错
This table has no primary key or unique index,无法分块同步
验证命令示例:SHOW KEYS FROM your_table WHERE Non_unique = 0 AND Seq_in_index = 1;
执行与收尾的关键动作
pt-online-schema-change 不是黑盒,几个参数决定成败:
- 基本命令:
pt-online-schema-change --alter "ENGINE=InnoDB" D=your_db,t=big_table --execute -
--chunk-size=1000控制每次迁移行数(大表建议调小至 500) -
--max-lag=1s防止从库延迟过大自动暂停;--critical-load="Threads_running=25"防止主库过载 - 迁移完成后,
.MYD/.MYI文件不会自动删除,必须人工确认新表COUNT(*)和校验和一致后再删
最易被忽略的是转换后的语义验证:外键脏数据是否触发约束失败、AUTO_INCREMENT 是否因重启重置、SELECT ... FOR UPDATE 锁行为是否与应用预期一致——这些都不会在 ALTER 或 pt-osc 过程中报错,而是在业务第一次执行对应 SQL 时才暴露。











