MySQL表碎片产生于MyISAM频繁DELETE/UPDATE残留空块,InnoDB则源于页内空闲空间与二级索引分裂空洞;DATA_FREE>0不等于需OPTIMIZE,应结合free_pct>25%、data_length>100MB、avg_row_length异常及COUNT差异等指标综合判断。
MySQL 表碎片是怎么产生的
表碎片主要出现在 MyISAM 和 InnoDB 引擎中,但成因不同:MyISAM 碎片来自频繁的 DELETE 或 UPDATE 导致的空闲数据块残留;InnoDB 则更多体现为页内空闲空间(DATA_FREE)和二级索引页分裂后的逻辑空洞。InnoDB 的聚簇索引本身会自动重组织,但大范围删除后仍可能留下可观的 DATA_FREE,影响扫描效率和缓冲池利用率。
常见误判是把 DATA_FREE > 0 当作必须 OPTIMIZE TABLE 的信号——其实 InnoDB 的 DATA_FREE 是以 extent(1MB)为单位预分配的,即使只用几 KB,也可能显示几百 MB 空闲,这不等于“碎片严重”。
如何准确判断是否需要 OPTIMIZE TABLE
真正值得触发 OPTIMIZE TABLE 的场景很有限,优先看这三个指标:
-
DATA_FREE / DATA_LENGTH > 0.25且DATA_LENGTH > 100MB(排除小表噪声) -
innodb_page_size默认 16KB,若avg_row_length远小于实际行宽(比如AVG_ROW_LENGTH是 200,但多数行含TEXT实际占数 KB),说明行溢出严重,页利用率低 -
SELECT COUNT(*)和SELECT COUNT(*) FROM t USE INDEX (PRIMARY)结果差异显著(>10%),暗示主键索引页存在大量已删除但未回收的记录(尤其在READ-COMMITTED或更低隔离级别下长期未 purge)
对 InnoDB,OPTIMIZE TABLE 实质是重建表(ALTER TABLE ... FORCE),会加 SX 锁、阻塞写入,且全程占用双倍磁盘空间。生产环境应避免定期执行,而应结合慢查询日志中全表扫描变慢、Handler_read_* 计数异常升高来反推。
监控碎片的实用 SQL 查询
以下查询可嵌入巡检脚本,注意过滤掉系统表和小表: