MyISAM崩溃后无法保证数据完整性,根本原因是它既无undo log回滚未完成操作,也无redo log重放已提交变更,所有修改直接写入.MYD/.MYI文件,崩溃时无法判断损坏位置或一致性状态。

MySQL崩溃后MyISAM无法保证数据完整性,根本原因不是它“修不回来”,而是它压根没有机制去判断“哪部分坏了”或“该不该回退”。
MyISAM 没有 UNDO LOG 和 REDO LOG
崩溃恢复依赖两类日志:UNDO LOG 用于回滚未完成的写入,REDO LOG 用于重放已提交但尚未刷盘的操作。MyISAM 完全不生成这两类日志——所有变更直接写入 .MYD(数据)和 .MYI(索引)文件。一旦写到一半断电,.MYD 可能多出半行,.MYI 可能指向错误偏移,而 MyISAM 自己毫无察觉。
常见表现包括:
-
Table is marked as crashed and should be repaired—— 这只是 MySQL 在打开表时发现索引头校验失败后的被动标记,不是主动检测结果 -
SELECT COUNT(*)和SELECT * FROM t WHERE id > 1000返回矛盾结果 —— 索引页计数与实际行数脱节,MyISAM 不验证一致性 - 主从复制中
REPLACE INTO在从库执行后状态错乱,却无任何错误日志 —— 因为 MyISAM 把语句当原子操作执行,不记录逻辑变更过程
REPAIR TABLE 不是恢复,而是暴力重建
REPAIR TABLE 命令不会比对事务边界、不会还原中间态,它只是按当前 .MYD 文件逐行读取,重新构建一套索引结构。如果 .MYD 本身已有损坏(如某行被截断、某字段值错位),重建后的索引只会把错误固化下来。
不同修复模式的实际行为差异:
-
REPAIR TABLE t;:默认模式,跳过损坏块,可能丢数据 -
REPAIR TABLE t EXTENDED;:逐行重建索引,但依然基于已损坏的.MYD,无法识别逻辑错误 -
REPAIR TABLE t USE_FRM;:完全丢弃原.MYI,仅靠.frm结构重建索引 —— 查询性能骤降,且无法修复.MYD层面的数据错位
崩溃后 MyISAM 甚至无法确认自己是否“脏”
InnoDB 崩溃重启时会在错误日志打印 InnoDB: Starting crash recovery...,并精确恢复到最近一个完整事务点。MyISAM 没有类似机制:它既不记录上次写入位置,也不在文件头保存校验和或事务序列号。所以即使 .MYD 和 .MYI 大小一致、文件存在,也无法证明二者内容逻辑匹配。
这就导致两个关键盲区:
- 应用层调用
SELECT FOR UPDATE,MyISAM 静默忽略,返回结果看似正常,实则并发写入已破坏数据一致性 - 备份工具如
mysqldump --single-transaction对 MyISAM 表自动退化为FLUSH TABLES WITH READ LOCK,整个库写入阻塞,且锁期间若发生崩溃,备份拿到的仍是可能错位的数据快照
真正难处理的不是“怎么修”,而是“修完之后你根本不知道还剩多少错”。MyISAM 的脆弱性不在崩溃那一刻,而在崩溃之后你仍把它当可靠数据源继续读写 —— 错误会层层放大,直到业务核对时才发现金额、状态、关联关系早已失真。











