索引碎片会显著增加磁盘I/O次数并降低缓冲池命中率;因页内空洞和物理存储不连续,MySQL需读取更多页才能获取有效数据,导致预读失效、EXPLAIN中rows虚高、ORDER BY变慢,且DATA_FREE不可靠,须结合平均行大小等指标综合判断。

索引碎片直接增加磁盘I/O次数
MySQL的InnoDB引擎用B+树组织索引,数据按页(默认16KB)存储。频繁INSERT/UPDATE/DELETE会导致页分裂和空洞——比如一页本可存100行,因多次随机写入只剩60行有效数据,剩下40行是“空隙”。查询时,MySQL仍需把整页读进buffer pool,但真正有用的数据只有一半。原本扫10页能拿到的数据,现在得读15–20页,I/O翻倍,尤其在机械盘或高并发场景下,延迟立刻拉高。
缓冲池命中率被悄悄拉低
碎片页填充率低,buffer pool里塞满大量“半空页”,有效数据密度下降。表现为Innodb_buffer_pool_read_requests飙升,而Innodb_buffer_pool_reads(真实磁盘读)也同步涨——比值跌破95%就是明显信号。更糟的是,预读(read-ahead)失效:InnoDB本想按物理连续性预加载相邻页,但碎片让逻辑顺序和物理位置脱节,预读页多数白读,缓存收益归零。
EXPLAIN里rows虚高,执行计划却“看起来正常”
碎片不改统计信息(Cardinality),所以EXPLAIN仍显示走索引、key_len合理、rows也没爆增。但实际扫描时,因页内空洞多,MySQL要遍历更多页才能凑够所需行数。典型现象:rows=50000但filtered=5,说明B+树遍历了大量无效页;或者ORDER BY变慢、Using filesort频发——都是页局部性崩坏的副作用。
DATA_FREE不是可靠指标,别光看它
DATA_FREE字段只反映表空间未分配的extent(最小1MB),对InnoDB碎片意义有限。小表DATA_FREE=1MB可能是正常预留,大表DATA_FREE=2GB也不代表真有2GB碎片——得交叉验证:(data_length + index_length) / table_rows算出平均行大小,若远超字段实际长度(比如VARCHAR(255)只存10字节却算出180字节),才是页内空洞的确凿证据。



















