alter table ... engine=innodb在mysql 8.0中不是原子操作,因其强制使用copy算法,全程持有mdl写锁阻塞读写,且不支持algorithm=inplace,大表易致磁盘满、超时、主从延迟飙升。

MySQL 8.0 中 MyISAM 表本身就不支持原子 DDL,这不是配置或语法问题,而是设计限制——你无法让它“支持”,只能绕开或替换。
为什么 ALTER TABLE ... ENGINE=InnoDB 会失败或卡住
直接对 MyISAM 表执行 ALTER TABLE t ENGINE=InnoDB 看似是迁移手段,但实际在 8.0 下极易出问题:
- 该语句强制走
COPY算法,全程持有 MDL 写锁,业务写入完全阻塞 - 若表大于 10GB,大概率触发磁盘满、超时中断、主从延迟飙升(
Seconds_Behind_Master跳到数小时) - 即使加了
LOCK=NONE,MySQL 也会自动降级为LOCK=EXCLUSIVE,因为引擎切换不支持无锁 - 执行中
SHOW PROCESSLIST显示状态为copy to tmp table,不是“准备中”,是真的在全量拷贝
系统表仍是 MyISAM 会导致 mysqld 启动失败
如果你的 mysql.user、mysql.db 等系统表还是 MyISAM 引擎,mysqld 根本不会进入服务状态:
- 错误日志里明确报
MY-010929 Storage engine 'MyISAM' does not support system tables,这是阻断性错误,不是警告 -
mysql_upgrade在 8.0.16+ 已废弃,执行它只会输出mysql_upgrade is deprecated,且对系统表无效 - 唯一可靠方式是用
--upgrade=FORCE参数启动 mysqld,并确保带全关键参数:--user=mysql --datadir=/var/lib/mysql --upgrade=FORCE - 必须用绝对路径调用新版本
mysqld,不能依赖systemctl start mysql,否则参数被忽略
非系统表的 MyISAM 迁移必须避开原生命令
业务库中普通 MyISAM 表要迁移到 InnoDB,不能靠 ALTER TABLE 硬扛:
- 确认
innodb_file_per_table为ON(5.6+ 默认开启),否则新表数据全写进ibdata1,后续无法收缩 - 磁盘剩余空间需 ≥ 原表
.MYD + .MYI总大小 × 2(含临时文件、redo log、undo log) - 若含
FULLTEXT索引,InnoDB 5.6+ 虽支持,但重建耗时翻倍,且分词逻辑与 MyISAM 不同,查不到结果可能不是 SQL 错,而是索引行为变了 - 真正可落地的方案只有两个:
pt-online-schema-change(适合 5.7/8.0 混合环境),或 MySQL 8.0.23+ 的ALGORITHM=INSTANT(仅限新增列等极轻量变更,不适用于引擎切换)
原子 DDL 报错如 ERROR 1033 / 1286 / 1872 的真实原因
这些错误看起来像语法或权限问题,其实往往指向底层一致性断裂:
-
ERROR 1033 (HY000): Incorrect information in file './db/t1.frm':InnoDB 检测到.ibd文件头与数据字典中记录的space_id不匹配 -
ERROR 1286 (42000): Unknown storage engine 'innodb':不是引擎不可用,而是mysql.innodb_table_stats表损坏或未加载 -
ERROR 1872 (HY000): Cannot truncate a table referenced in a foreign key constraint:实则是INFORMATION_SCHEMA.INNODB_FOREIGN缓存未刷新,原子 DDL 强制校验该视图一致性 - 排查顺序必须是:
INFORMATION_SCHEMA.INNODB_SYS_TABLES→INFORMATION_SCHEMA.FILES→ls -l /var/lib/mysql/db/t1.ibd,跳步容易误判
最常被忽略的一点:原子 DDL 的“原子性”只保证元数据变更和物理文件操作打包成事务回滚,它不等于“不锁表”“不阻塞”“可在线”。只要涉及 MyISAM,你就已经站在原子 DDL 的能力边界之外了——迁移不是技术选型问题,而是生存前提。











