直接用alter table...engine=innodb循环执行在生产环境大概率锁库、拖垮主从、触发磁盘爆满,因myisam→innodb跨引擎转换不走inplace而需全表拷贝,导致mdl写锁阻塞读写、从库延迟飙升、磁盘空间翻倍。

直接用 ALTER TABLE ... ENGINE=InnoDB 循环执行,在生产环境大概率会锁库、拖垮主从、触发磁盘爆满——这不是脚本写得不对,是没绕开 MySQL DDL 的底层限制。
MyISAM → InnoDB 为什么不能用简单循环 ALTER?
MySQL 5.6+ 虽支持 ALGORITHM=INPLACE,但跨引擎转换(如 MyISAM → InnoDB)不走 inplace 流程,仍需全表拷贝。现象包括:
- 主库执行时持 MDL 写锁,所有对该表的读写全部阻塞
- 从库回放该 DDL 时同样卡住,
Seconds_Behind_Master瞬间飙升到几小时 - 临时需要等量磁盘空间(原表大小 ×2),
innodb_file_per_table=OFF时还会污染系统表空间 -
SHOW PROCESSLIST里卡在copy to tmp table状态
生成 ALTER 语句时必须处理的 SQL 注入风险
用 information_schema.tables 拼 SQL 是主流做法,但表名或库名含特殊字符(如横线、空格、关键字 order、group)会导致语法错误甚至误删。必须:
- 对表名用
QUOTE(table_name)包裹,生成形如ALTER TABLE `order` ENGINE=InnoDB; - 对库名同样用
QUOTE(table_schema),拼成ALTER TABLE `my-db`.`user_log` ENGINE=InnoDB; - 脚本输出后务必人工检查:
head -20 alter_engine.sql看前几行是否合法,再grep -n "ERROR\|Warning" alter_engine.sql排查生成异常
真正能落地的批量转换方案选型
没有“一键通用”,必须按环境能力分层选型:
- 线上不能停服:必须用
pt-online-schema-change,它通过影子表 + 触发器实现无锁,但要求表有主键、无外键、binlog_format != STATEMENT - 离线窗口充足:用
mysqldump --no-create-info --skip-triggers导出数据,建新表时显式指定ENGINE=InnoDB,再导入 - 小表(ALTER TABLE t ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE,但 MyISAM→InnoDB 仍 fallback 到 copy,仅降低锁粒度
执行时容易被忽略的细节
即使选对方案,以下三点一错就全崩:
- 别一次性跑完:按
data_length + index_length分档(如 1GB 一档),先用sed -n '1,50p'试前 50 行 - 执行命令加超时和包大小限制:
mysql --connect-timeout=10 --max-allowed-packet=512M -u root -p -e "source alter_engine.sql" - 转换后必须验:
SELECT table_name, engine FROM information_schema.tables WHERE table_schema='db_name' AND engine!='InnoDB';,同时确认innodb_buffer_pool_size是否足够、自增列行为是否一致、全文索引是否需重建











