myisam 不支持事务,因缺失 undo log、redo log 和事务 id;切 innodb 时需处理 null 主键、全文索引参数、标识符语法及磁盘空间;批量切换应生成 sql、分批执行并检查 autocommit、客户端配置与 orm 事务代理。

MyISAM 表根本无法支持事务——不是配置问题,是引擎本身没实现 undo log、redo log 和事务 ID 机制。想用事务,必须切到 InnoDB。
为什么 ALTER TABLE ... ENGINE=InnoDB 会失败
直接执行 ALTER TABLE t ENGINE=InnoDB 报错,常见原因不是语法问题,而是表结构与 InnoDB 不兼容:
-
PRIMARY KEY或UNIQUE KEY中包含NULL值:InnoDB 不允许主键字段为NULL,而 MyISAM 允许(哪怕不合理);报错如ERROR 1171: Primary key must not contain NULL values - 全文索引字段的最小词长不匹配:
innodb_ft_min_token_size默认是 3,而 MyISAM 的ft_min_word_len可能是 2,转换后搜索行为会变,但不会报错,容易被忽略 - 表名或字段名用了反引号但未正确包裹:比如
ALTER TABLE `user-table` ENGINE=InnoDB中的短横线需确认是否在标识符内合法,否则触发 SQL 解析错误 - 磁盘空间不足:大表转换会在
tmpdir生成临时拷贝,ALTER过程中可能耗尽空间并中断
如何安全批量切换多个 MyISAM 表到 InnoDB
线上环境不能逐条手敲 ALTER,也别用 phpMyAdmin 点点点——容易漏、不可复现、难审计。推荐用 SQL 生成 + 分批执行:
- 先查出所有 MyISAM 表:
SELECT CONCAT('ALTER TABLE `', TABLE_NAME, '` ENGINE=InnoDB;') FROM information_schema.tables WHERE table_schema = 'your_db' AND engine = 'MyISAM'; - 导出结果后,人工检查是否有含
NULL主键的表(用SHOW CREATE TABLE确认),提前修复数据或改字段为NOT NULL - 对单表 >1GB 的情况,加
ALGORITHM=INPLACE, LOCK=NONE(MySQL 8.0+ 支持),避免锁表;5.7 及以前默认是COPY算法,必须停写 - 不要一次性跑全部语句——拆成每 5–10 张表一组,在低峰期分批执行,并监控
SHOW PROCESSLIST和磁盘 I/O
切换后事务仍不生效?检查这三个地方
引擎改完了,BEGIN/COMMIT 还是没反应,大概率卡在这三处:
-
autocommit被设为1:即使 InnoDB 表,INSERT也会立即提交。用SELECT @@autocommit;确认,必要时显式执行SET autocommit = 0; - 客户端连接未开启事务模式:JDBC URL 没加
?useSSL=false&serverTimezone=UTC等参数没问题,但若漏了&rewriteBatchedStatements=true或驱动版本太老,可能静默降级回 MyISAM 兼容路径 - GORM / Spring Boot 等 ORM 层绕过事务控制:比如 GORM 的
tx.Create()在事务函数外调用,或 Spring 中@Transactional方法被本类非 public 方法调用(自调用失效),此时即使表是 InnoDB,事务代理也压根没启动
最易被忽略的是:MyISAM 表上执行 START TRANSACTION 不报错,也不起作用——MySQL 会静默忽略。所以不能只看“有没有报错”,得验证 ROLLBACK 后数据是否真消失。换引擎只是必要条件,不是充分条件。











