mysql索引损坏是底层存储或硬件异常的明确信号,非高负载自然结果;需通过check table extended、错误日志分析及innochecksum等工具区分页级物理损坏与逻辑失效,且损坏多发生于二级索引页。

MySQL 在高负载下出现索引损坏,**不是性能瓶颈的自然结果,而是底层存储或硬件异常的明确信号**。它不发生在“查询变慢”阶段,而往往紧随一次 crash、kill -9 强制终止、磁盘 I/O 超时、或 RAID 卡掉盘之后——此时 B+ 树页的校验和(page checksum)已无法通过,InnoDB 拒绝加载该页,报错如 InnoDB: Database page corruption 或 Incorrect key file for table。
如何确认是页级损坏而非逻辑失效?
先区分「索引没被用」和「索引不能用」:前者是优化器跳过索引(EXPLAIN 中 key 为 NULL),后者是 MySQL 启动失败、查询直接报错、或 CHECK TABLE 返回 status = 'corrupt'。
- 运行
CHECK TABLE t1 EXTENDED—— 若返回OK但查询仍慢,大概率是逻辑失效;若返回corrupt或error,就是物理损坏 - 查错误日志:
grep -i "corrupt\|checksum\|page.*fail" /var/log/mysql/error.log - InnoDB 表看
information_schema.INNODB_SYS_TABLESPACES中FILE_SIZE与磁盘.ibd文件大小是否一致;不一致说明写入截断
高负载如何触发页损坏?关键在 I/O 链路断裂
高并发写入本身不会损坏索引,但会放大底层缺陷。真正致因是以下任一环节在压力下暴露:
- SSD/NVMe 的写缓存未持久化(
write cache enabled且无电容保护),断电后部分页写入一半 - RAID 卡电池失效或 write-back 模式未降级为 write-through,导致缓存数据丢失
- 文件系统(如 ext4)挂载时未加
barrier=1或data=ordered,日志与数据页落盘顺序错乱 - 内核 I/O 调度器(如
deadline)在高队列深度下丢弃部分 bio 请求,无任何错误返回
这些故障在低负载时可能被掩盖,一旦 QPS 上升、I/O 延迟抖动加剧,就容易卡在某个页的 double-write buffer 刷盘失败点上。
绕过损坏页继续服务的临时手段(慎用)
生产环境不能停机修复时,可尝试隔离损坏页,但必须清楚代价:
- 对 InnoDB 表:设
innodb_force_recovery = 4启动(仅允许 SELECT,禁写),然后mysqldump导出可用数据,再重建库 - 若知道损坏页号(日志里有
page number XXXX),可用debug_sync或 Percona Toolkit 的pt-table-checksum跳过单页校验(需源码级修改,不推荐) - 绝对不要对损坏表执行
OPTIMIZE TABLE或ALTER TABLE ... ENGINE=InnoDB—— 这会强制读取所有页,必然 crash
为什么 CHECK TABLE 不总能发现损坏?
CHECK TABLE 默认只校验索引结构(B+ 树指针连通性),不验证每个页的 checksum 和 LSN 连续性。真正可靠的是:
- 启动时 InnoDB 自检:从
ibdata1头部读取 space ID,逐页比对 checksum(受innodb_checksum_algorithm控制) - 手动触发完整校验:
mysqlcheck --check --extended db_name t1(MyISAM)或innochecksum -v /var/lib/mysql/db/t1.ibd(InnoDB 原生工具) - 云厂商 RDS 通常禁用
innochecksum,需依赖其后台巡检日志
最易被忽略的一点:**损坏常发生在二级索引页,而非聚簇索引页**。因为二级索引更新更频繁、缓冲池淘汰更激进,而 CHECK TABLE 默认不 deep-scan 二级索引 leaf pages,除非加 EXTENDED 参数。











