最直接信号是slave_sql_running: no且last_sql_error出现myisam相关错误(如错误1296、"table is read only"等),seconds_behind_master为null;必须主库执行alter table engine=innodb彻底解决,禁用跳过或仅改从库引擎。

查 SHOW SLAVE STATUS\G 里是否卡在 MyISAM 表上
最直接的信号是 Slave_SQL_Running: No,且 Last_SQL_Error 明确提到 MyISAM 相关错误,比如:Got error 1296 from storage engine、Table is read only、Cannot execute statement: binlogging impossible since the storage engine of table ... does not support it。这些不是偶发 IO 错误,而是引擎能力缺失导致的硬性拒绝。
注意 Seconds_Behind_Master 可能为 NULL(不是 0 或数字),这是 SQL 线程已停止的典型表现;别被 “IO 线程还在跑” 欺骗,只要 SQL 线程停了,数据就不再同步。
- 执行
SHOW SLAVE STATUS\G后重点看三行:Slave_IO_Running、Slave_SQL_Running、Last_SQL_Error - 如果
Last_SQL_Error里出现表名,立刻去主从库分别查该表引擎:SHOW CREATE TABLE `db_name`.`table_name` - 不要只查从库——主库用 MyISAM、从库也用 MyISAM 并不安全,因为复制格式(如 ROW)本身就不支持 MyISAM 回放
确认 binlog_format 是不是 ROW 或 MIXED
MyISAM 在 STATEMENT 格式下可能“暂时跑通”,但一旦遇到 NOW()、UUID()、用户变量或非确定性函数,从库执行就会出错。而 MySQL 5.7+ 对 ROW 格式做了严格校验:只要表引擎不支持行事件重放(MyISAM 就不支持),SQL 线程启动时就会直接报错,根本不会尝试执行。
- 查当前格式:
SELECT @@binlog_format;,结果为ROW或MIXED就必须处理 MyISAM 表 - 不要试图改成
STATEMENT来绕过——这会掩盖问题,且无法解决非确定性语句引发的从库执行偏差 -
MIXED不是保险箱:它自动降级为 STATEMENT 的逻辑不可控,出错概率反而更高
主库上执行 ALTER TABLE ... ENGINE=InnoDB 的实操要点
这不是可选项,是唯一安全路径。MyISAM 转 InnoDB 必须在主库执行,否则从库执行 DML 时仍会因引擎不兼容中断。但这个操作会锁表,影响业务写入,必须谨慎。
- 先评估表大小:
SELECT table_name, round(((data_length + index_length) / 1024 / 1024), 2) AS size_mb FROM information_schema.tables WHERE table_schema = 'your_db' AND table_name = 'your_table'; - 大表(>50GB)建议先在从库停掉 SQL 线程后单独转换,验证无误再在主库低峰期执行,避免主库长时间不可写
- 禁止用
mysqldump导出导入方式转换——会丢失自增 ID、触发器、binlog 位置,导致主从 GTID 或 position 错乱 - 执行时加
ALGORITHM=COPY显式声明(MySQL 5.6+ 默认如此),避免误以为支持 INPLACE(MyISAM 转 InnoDB 不支持)
为什么不能跳过错误或只改从库引擎
跳过 MyISAM 相关事件(如用 SET GLOBAL sql_slave_skip_counter = 1)看似快,实则埋雷:后续所有对该表的 DML 都会继续失败,且跳过的 binlog 事件无法重放,主从数据必然分叉。只改从库引擎更危险——主库仍是 MyISAM,下次 DDL(如 ADD COLUMN)或 DML(如 UPDATE ... ORDER BY)仍会因引擎行为差异导致从库崩溃或数据错乱。
真正麻烦的点在于:MyISAM 缺乏事务和崩溃恢复能力,它的锁表机制、崩溃后修复逻辑与 InnoDB 完全不同。即使某次复制侥幸成功,下一次主库重启或异常中断后,从库状态就可能彻底不可信。











