可实现业务基本无感的MyISAM转InnoDB:需用ALGORITHM=INPLACE+LOCK=NONE(满足条件时免锁)或ALGORITHM=COPY+LOCK=SHARED(读不阻塞),避开FULLTEXT等兼容陷阱,并分步校验替换。

不能完全“不停服”,但可以做到业务基本无感——关键在用对 ALTER TABLE 语法和避开 MyISAM 兼容陷阱。
ALTER TABLE ENGINE=InnoDB 是否锁表?取决于 MySQL 版本和参数
MySQL 5.6+ 支持在线 DDL,但默认 ALTER TABLE table_name ENGINE=InnoDB 仍会触发表级写锁(尤其 MyISAM → InnoDB 转换)。真正“低影响”的写法必须显式指定算法和锁级别:
- 用
ALGORITHM=INPLACE+LOCK=NONE才可能免锁(仅当满足 InnoDB 行格式、无全文索引、无外键依赖等条件) - 更稳妥的组合是
ALGORITHM=COPY+LOCK=SHARED:允许并发读,阻塞写,比全锁友好得多 - 若表含 FULLTEXT 索引,InnoDB 不兼容 MyISAM 的全文语法,
ALGORITHM=INPLACE会直接报错ER_UNSUPPORTED_ALTER_INPLACE_ON_FULLTEXT - 执行前先查是否支持:
SELECT * FROM information_schema.INNODB_TRX;确保没长事务阻塞 DDL
为什么 Navicat 或 phpMyAdmin 点一下就卡死?
图形工具默认调用裸 ALTER TABLE ... ENGINE=InnoDB,不加任何在线参数。遇到大表(>50MB)或高并发写入时,它会:
- 默默启用
ALGORITHM=COPY(重建表),期间持有SUPER级锁 - 触发临时磁盘暴涨(ibtmp1 + sort_buffer 内存不足时落盘)
- 阻塞所有
INSERT/UPDATE/DELETE,但SELECT可能还在跑——导致你误以为“没锁”,其实写已挂起 - 若超时中断,可能留下半成品
#sql-xxx临时表,需手动清理
大表安全切换的最小可行路径
别信“一键转换”,300MB 以上 MyISAM 表必须分步控风险:
- 先备份单表:
mysqldump --single-transaction --no-create-info db_name table_name > table_name.sql - 建新表并设引擎:
CREATE TABLE table_name_new LIKE table_name; ALTER TABLE table_name_new ENGINE=InnoDB; - 用
INSERT IGNORE INTO table_name_new SELECT * FROM table_name;分批导(加LIMIT和SLEEP防抖动) - 校验行数和 checksum:
CHECKSUM TABLE table_name;vsCHECKSUM TABLE table_name_new; - 原子替换:
RENAME TABLE table_name TO table_name_old, table_name_new TO table_name;
最容易被忽略的兼容性断点
MyISAM 切到 InnoDB 后,应用可能立刻出问题,不是引擎没切成功,而是语义变了:
-
AUTO_INCREMENT初始值行为不同:MyISAM 重启后从 max(id)+1 开始;InnoDB 从内存缓存值开始(可能跳号) -
COUNT(*)性能暴跌:MyISAM 存元数据,秒回;InnoDB 得扫聚簇索引,大表可能几秒甚至几十秒 - 全文检索字段必须重建:
DROP FULLTEXT INDEX再ADD FULLTEXT INDEX,否则查询返回空 - 如果原表有
DELAY_KEY_WRITE=1(MyISAM 特有),InnoDB 不识别,SHOW CREATE TABLE会报 warning 并忽略
真正耗时的从来不是那条 ALTER 语句,而是确认哪些表用了全文索引、哪些 COUNT 被前端当缓存用、哪些 ORM 还在靠 MyISAM 的崩溃恢复逻辑做兜底——这些藏在代码里的耦合,比改配置难十倍。


















