ALTER TABLE ENGINE=InnoDB在大表上必然失败,因其强制使用COPY算法:全程MDL写锁阻塞读写、磁盘空间需翻倍、主从延迟剧增;仅≤500MB且无FULLTEXT/有主键的小表可谨慎尝试,大表必须用pt-online-schema-change实现无锁迁移。

别直接在生产大表上跑 ALTER TABLE t ENGINE=InnoDB —— 它会锁死写入、吃光磁盘、拖垮主从,且无法真正在线。
为什么 ALTER TABLE ENGINE=InnoDB 在大表上必然失败
MySQL 5.7/8.0 下,MyISAM → InnoDB 的引擎转换不支持 ALGORITHM=INPLACE,强制走 COPY 算法:先建新表结构,再逐行拷贝数据,最后原子替换。这个过程带来三个硬伤:
- 全程持有 MDL 写锁:
INSERT/UPDATE/DELETE全部阻塞,SELECT在部分版本和隔离级别下也会被卡住 - 磁盘空间需求翻倍:需同时容纳原
.MYD+.MYI和新.ibd文件,外加 redo log 临时增长 - 主从延迟爆炸:主库锁表期间积压 binlog,从库回放时同样要等锁释放,
Seconds_Behind_Master可能跳到数千秒
一个 10GB MyISAM 表,转换常耗时 15–40 分钟——这已超出绝大多数业务低峰窗口容忍范围。
哪些表可以勉强用 ALTER TABLE 直接转
仅限满足全部以下条件的小表:
- 表大小 ≤ 500MB(实测在 SSD 上通常 ≤ 3 分钟)
- 业务允许该表在转换期间完全不可写(非核心日志、配置类只读表)
- 确认无
FULLTEXT索引(否则ALTER会直接报错退出) - 已有显式主键或唯一非空索引(避免 InnoDB 静默创建不可控的隐藏
ROW_ID) - 磁盘剩余空间 ≥ 原表
.MYD文件大小 × 2.2(留出日志与碎片余量)
执行前务必先在从库验证:SHOW PROCESSLIST 观察是否长期卡在 copy to tmp table;并用 df -h 确认 /var/lib/mysql 所在分区空间充足。
大表必须用 pt-online-schema-change 替代
这是目前最成熟、可控的线上无锁迁移方案。它不依赖 MySQL 原生 DDL,而是通过影子表 + 触发器双写实现平滑过渡:
- 原表持续可读写,业务无感知
- 数据一致性由触发器保障,无需停写
- 支持分块迁移(
--chunk-size)、延迟控制(--max-lag)、超时重试
但必须满足前提:
- 目标表必须有主键或唯一非空索引(否则
pt-osc无法分片同步,报错This table has no primary key or unique index) - 禁用
LOAD DATA INFILE或REPLACE INTO等绕过触发器的操作 - 确保
innodb_file_per_table=ON(MySQL 5.6+ 默认开启),否则新表无法生成独立.ibd - 命令示例:
pt-online-schema-change --alter "ENGINE=InnoDB" D=your_db,t=big_table --execute
注意:pt-osc 迁移后不会自动删除旧的 .MYD/.MYI 文件,需人工确认数据一致后手动清理。
迁移后最容易被忽略的三件事
引擎变了,行为全变——只改 ENGINE 字段远远不够:
-
FULLTEXT索引必须重建:innodb_ft_min_token_size默认是 3,而 MyISAM 是 4,搜索结果会不同;原索引无效,得先DROP INDEX再ADD FULLTEXT -
AUTO_INCREMENT列必须是索引一部分:若原表只有id INT AUTO_INCREMENT但没建任何索引,ALTER会报ERROR 1075;迁移前得补PRIMARY KEY(id)或INDEX(id) - 查询性能可能倒退:比如高频
SELECT COUNT(*) FROM t,MyISAM 是元数据直取,InnoDB 要扫索引;若没覆盖索引,必须重构查询或加缓存
真正麻烦的从来不是命令怎么敲,而是那些没报错、却悄悄改变语义的细节。











