并非所有MyISAM表都需迁移;只读或极小表(≤1GB)可不迁,否则须用pt-online-schema-change无锁转换;MySQL 8.0+中ENGINE变更仍需COPY而非INPLACE;迁移后须调优innodb_buffer_pool_size(50%–75%物理内存)、降低key_buffer_size、酌情设innodb_flush_log_at_trx_commit=2,并处理FULLTEXT语法差异、NOT NULL主键约束、INSERT DELAYED替换及事务隔离与隐式提交问题。

直接执行 ALTER TABLE t ENGINE=InnoDB 在 MySQL 8.0 中仍会锁表、拷贝全量数据,且不支持 ALGORITHM=INPLACE —— 这不是“平滑”,而是高风险操作。真正能落地的平滑迁移,必须绕开原生命令的硬限制,优先用工具兜底,再针对性调参和验证。
怎么判断哪些 MyISAM 表真该迁?
不是所有 MyISAM 表都值得动。先跑这句查出存量:
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');
重点关注三类表:
- 有写入(
table_rows变动频繁)或外键依赖的表 - 数据量 >1GB 且业务要求高可用的表
- 需要事务、行级锁或崩溃恢复能力的表
只读小表(如配置表、字典表 ≤100MB)、带 FULLTEXT 且没升级到 MySQL 5.6+ 的旧环境,可暂缓迁移——强行转 InnoDB 反而增加索引重建开销和内存争抢。
大表迁移为什么不能用 ALTER TABLE?
ALTER TABLE t ENGINE=InnoDB 在 MySQL 8.0 下仍是 COPY 算法:创建新表 → 全量拷贝 → 重建索引 → 重命名 → 清理旧文件。过程中:
-
SHOW PROCESSLIST显示copy to tmp table,不是“准备中”,是真的在搬数据 - 持有 MDL 写锁,阻塞所有
INSERT/UPDATE/DELETE,SELECT在 RR 隔离级别下也可能被阻塞 - 磁盘空间需 ≥ 原表
.MYD + .MYI总大小 ×2(含 redo/undo/临时文件),10GB 表就得预留 20GB+ - 即使加
LOCK=NONE,MySQL 也会自动降级为LOCK=EXCLUSIVE,因为引擎切换本身不支持无锁
生产环境高峰期跑这个,等于主动制造服务中断。
用 pt-online-schema-change 实现无锁迁移
这是目前最稳妥的方案,它通过影子表 + 触发器同步变更,业务几乎无感。前提条件:
- 确保
innodb_file_per_table=ON(MySQL 5.6+ 默认开启) - 目标表必须有主键或唯一非空索引(否则触发器无法定位行)
- 禁用
FOREIGN_KEY_CHECKS和UNIQUE_CHECKS(工具内部会处理)
典型命令:
pt-online-schema-change --alter "ENGINE=InnoDB" D=your_db,t=large_table --execute
关键控制点:
- 加
--chunk-size=1000控制每批处理行数,避免单次事务过大 - 加
--max-load="Threads_running=25"防止从库延迟飙升 - 加
--check-interval=5让工具每 5 秒检查一次负载,超限自动暂停
执行完务必检查 information_schema.TABLES 确认 ENGINE 已变,并用 pt-table-checksum 核对主从数据一致性。
迁移后必调的 InnoDB 参数与陷阱
不调参就上线,性能可能比 MyISAM 还差。重点改三项:
-
innodb_buffer_pool_size设为物理内存的 50%–75%,低于 2GB 就别迁了——InnoDB 没缓存就是硬盘慢查询 -
key_buffer_size从 MyISAM 时代的 256M 或 512M,砍到 32M 甚至 8M,避免内存被无效占用 -
innodb_flush_log_at_trx_commit非金融场景设为2(每秒刷日志),写吞吐能翻倍;设0虽更快但崩溃可能丢 1 秒数据
容易被忽略的细节:
-
FULLTEXT索引必须手动重建,InnoDB 默认innodb_ft_min_token_size=3,MyISAM 是ft_min_word_len=4,搜“it”可能查不到 - 自增列必须是索引一部分,若原表只有
id INT AUTO_INCREMENT但没建索引,ALTER直接报ERROR 1075 -
SELECT COUNT(*)在 InnoDB 上不再走元数据快照,高频调用要加覆盖索引或改用近似统计
迁移不是改完引擎就结束,而是从存储层开始重新校准整个数据库行为——尤其是应用是否还在隐式提交、有没有漏捕获 Lock wait timeout exceeded 错误。


















