REPAIR TABLE EXTENDED 必须全表扫描,因 MyISAM 的 .MYI 不存数据校验和与行偏移信息,索引严重错乱时只能丢弃索引树、逐行读取 .MYD 重建索引,过程绕过缓存、触发大量磁盘寻道与写放大,I/O 成主要瓶颈。

REPAIR TABLE EXTENDED 为什么必须全表扫描
因为 MyISAM 的 .MYI 索引文件不存数据校验和,也不记录每行在 .MYD 中的物理偏移是否有效。当索引结构严重错乱(比如 key block 损坏、delete-link 链断裂),MySQL 无法靠局部修复恢复逻辑一致性,只能丢弃整个索引树,再逐行读取 .MYD 文件重建——这就是 EXTENDED 模式的本质。
这个过程不是“修索引”,而是“重做索引”:
- MySQL 打开
.MYD,按固定行长或变长格式解析每一行原始字节 - 对每行提取索引列值,重新计算哈希/排序键,插入新索引块
- 期间不做任何数据有效性校验(如 NOT NULL、CHECK 约束)
- 若某行数据本身已损坏(例如字段长度溢出、UTF-8 字节序列非法),可能直接导致插入失败或静默截断
USE_FRM 模式下重建索引更慢的真正原因
REPAIR TABLE ... USE_FRM 看似只是“用表结构重做索引”,实际会触发最重的重建路径:它完全忽略原有 .MYI 和 .MYD 的内部一致性,强制从头开始——先创建空索引文件,再完整遍历 .MYD 并逐行重建所有索引(包括 FULLTEXT 和前缀索引)。
相比 EXTENDED,它的额外开销在于:
- 跳过所有快速校验(如
myisamchk -s能跳过的 header 检查) - 对每个索引字段重复解析同一行数据(例如联合索引
(a,b,c)会三次提取字段值) - 全文索引重建需额外分词、倒排、压缩,CPU 和 I/O 压力陡增
- 无并发控制,全程单线程执行,无法利用多核
为什么磁盘 I/O 是主要瓶颈,而不是 CPU
MyISAM 表修复的耗时几乎全部卡在磁盘读写上,尤其当表远大于内存时:
-
.MYD是纯顺序存储,但修复时 MySQL 按行随机访问(因索引插入顺序打乱)→ 触发大量磁盘寻道 - 重建的
.MYI是 B-tree 结构,写入时频繁触发索引块分裂和重分配 → 写放大明显 - 临时文件(如
repair_xxx.MYI)与原文件在同一磁盘分区 → 读写竞争加剧 - 系统 page cache 无法复用:修复过程绕过大部分缓存逻辑,直接调用
pwrite()/pread()
实测中,一块 SATA SSD 上修复 50GB MyISAM 表,I/O wait 占用常超 90%,而 CPU idle 仍在 60% 以上。
容易被忽略的三个性能陷阱
很多运维以为“加内存就能提速”,其实修复速度基本不受 buffer pool 或 key_buffer_size 影响,真正卡点在底层文件操作:
- 不要在修复时启用
innodb_flush_log_at_trx_commit=1—— 这是 InnoDB 参数,对 MyISAM 无效,还可能误导配置 -
myisam_sort_buffer_size只影响排序阶段(如OPTIMIZE TABLE),REPAIR TABLE完全不使用它 - 如果
.MYD文件有碎片(例如 ext4 上未开启dir_index),lseek()+read()的系统调用延迟会指数级上升,此时myisamchk --safe-recover可能比 SQL 方式快 3–5 倍
最现实的优化只有两个:把 .MYD 和临时修复目录放在不同物理磁盘;修复前用 hdparm -I /dev/sdX 确认磁盘未启用了写缓存禁用(WCE=0)。


















