myisam .myd文件损坏时repair table基本无效,必须停服后用myisamchk --safe-recover抢救;修复后需人工核验数据完整性,因无事务和校验机制,跳过坏块不保证业务正确。

MyISAM数据文件(.MYD)损坏不能只靠REPAIR TABLE硬修,它大概率失败——因为REPAIR TABLE EXTENDED只能重建索引、重排有效行,但对已写坏或校验失败的数据块无能为力。
看到“record delete-link chain broken”就说明.MYD真坏了
这个错误不是警告,是确诊书:record delete-link chain broken意味着数据文件里记录的链表指针断裂,MySQL读到一半就卡住。此时REPAIR TABLE table_name QUICK会直接跳过,REPAIR TABLE table_name(默认QUICK)也基本无效。
-
CHECK TABLE table_name返回该提示,且Msg_text里没提key file,说明问题在.MYD,不在.MYI - 用
myisamchk -s /var/lib/mysql/dbname/table_name.MYI检查时,会报Found block with wrong size或Record-count is not correct - SELECT COUNT(*)卡住或返回明显偏少的数字,但
SHOW TABLE STATUS里的Data_length仍很大——说明有大量“不可读但占空间”的坏块
别在MySQL运行时硬试REPAIR TABLE EXTENDED
REPAIR TABLE table_name EXTENDED确实会逐行扫描.MYD并重建整个表,但它有个致命前提:所有数据块必须能被MySQL底层IO层完整读出。一旦遇到物理损坏、扇区错误或部分字节翻转,它就会中断并报Operation failed,且不回滚,可能让表彻底变ERROR 1033状态。
- 执行前必须确认
@@tmpdir剩余空间 ≥Data_length的2倍——EXTENDED模式会生成table_name.TMD临时文件,空间不足就静默失败 - 如果
.MYD文件本身属主不是mysql用户(比如root拷贝过),修复过程会因权限拒绝中断,错误日志里只写Errcode: 13,不提示具体原因 - 修复后即使返回
OK,也得立刻跑ANALYZE TABLE和EXPLAIN SELECT ... WHERE indexed_col = ?验证索引是否真能命中——很多“修复成功”的表,查询时仍走全表扫描
真坏了就得切到myisamchk --safe-recover
当REPAIR TABLE EXTENDED失败,唯一靠谱的抢救路径是停MySQL,用myisamchk底层恢复。它不依赖MySQL服务,直接操作文件,能跳过坏块、提取可读行。
- 先停服务:
systemctl stop mysql,确保没有进程锁着.MYD/.MYI - 执行:
myisamchk --safe-recover --force /var/lib/mysql/dbname/table_name.MYI——--safe-recover会尝试从.MYD中逐块读取,遇到校验失败自动跳过;--force防止中途因单行错误退出 - 若提示
Key cache not large enough,加--key_buffer_size=256M(根据内存调);若报Can't lock file,检查文件属主:chown mysql:mysql *.MYD *.MYI *.frm - 恢复完成后,
myisamchk -s再扫一遍,确认输出只有OK;然后systemctl start mysql,立刻用CHECK TABLE验证
修复后必须人工核对数据完整性
MyISAM没有事务、没有checksum校验,myisamchk --safe-recover抢救出来的数据,只是“能解析的行”,不保证业务逻辑正确。比如时间字段错位、金额符号反转、JSON字段截断,工具根本不会报错。
- 对比修复前后
SELECT COUNT(*),差值超过5%就得警惕——--safe-recover跳过的坏块不会计入新表 - 抽样查关键业务字段:
SELECT id, amount, created_at FROM table_name ORDER BY id DESC LIMIT 100;看是否有NULL、0、负数或乱码 - 如果原表有自增主键,检查最大ID是否连续;若有唯一索引,跑
SELECT col, COUNT(*) FROM table_name GROUP BY col HAVING COUNT(*) > 1查重复 - 最保险的做法:从最近一次可用备份里导出
SELECT * FROM table_name WHERE id > [last_known_good_id],补进修复后的表
真正麻烦的不是命令怎么敲,而是你永远不知道.MYD里哪一行是最后能读出来的——所以只要没做定期校验和备份,每次myisamchk --safe-recover都是一次赌注。











