bigywater.tech
MySQL / InnoDB · 75

表碎片和空间回收

删一半的行,同时追踪文件大小和DATA_FREE这个常被当成碎片指标的字段,看它到底能不能真实预警表碎片。

DATA_FREE全程为0
真实测试中这个字段完全没有反映出碎片
约28.6%
OPTIMIZE前后真实的文件缩小比例

做的事:删一半的行,追踪三个真实指标

代码-- frag_test 有1000行 DELETE FROM frag_test WHERE id % 2 = 0; -- 真实删掉一半 -- 追踪:真实的文件大小 / TABLE_ROWS / DATA_FREE

验证:删了一半的行,文件大小和DATA_FREE真实都没反应

实测 · 本机执行输出
删除前:  TABLE_ROWS=1000  文件=344064字节  DATA_FREE=0
删除后:  TABLE_ROWS=500   文件=344064字节  DATA_FREE=0   ← 两者真实都没变化!

本以为删掉一半数据,至少information_schema.TABLES里的DATA_FREE字段会真实反映出"这么多空间理论上可以回收",结果它全程真实都是0。这是一个容易被误解的真实细节:DATA_FREE在InnoDB独立表空间下,反映的是表空间文件里完整空闲的区(extent)数量,而不是删除操作在页面内部留下的、还没被回收成完整空闲区的零散空间——删除留下的洞,绝大多数情况下都停留在"页面内部有空隙,但整个页面还没空到能算作空闲区"这个状态,不会被这个字段真实统计进去。

验证:真正能看出碎片的,只有OPTIMIZE前后的真实对比

实测 · 本机执行输出
OPTIMIZE TABLE frag_test 之前:  文件=344064字节(500行数据,但文件是1000行时期的大小)
OPTIMIZE TABLE frag_test 之后:  文件=245760字节,DATA_FREE依然是0

真实缩小了约28.6%
OPTIMIZE前后的文件大小差(344064→245760,缩小约28.6%),才是这张表真实存在过的碎片量的直接证据——但DATA_FREE这个字段从头到尾都没能提前告诉你这一点。这是本文想诚实提醒的一点:不要依赖DATA_FREE来判断一张表是否需要OPTIMIZE TABLE,它在很多场景下(至少本次这种独立表空间、页面内部零散删除的场景)真实不够灵敏。更可靠的判断方式是真实对比数据字典里的逻辑行数估算大小(TABLE_ROWS × 平均行长)和磁盘上真实的文件大小——如果真实文件大小明显超过逻辑数据量该占的大小,才说明碎片确实存在。

分步演示:三个阶段,DATA_FREE始终没变过

每一步都是本文上面用真实文件大小和information_schema跑出来的原始结果。

DATA_FREE这次真实没能提前预警碎片
这一步新验证的状态

    本文用到的工具

    • 本机 MySQL 26.7.0(InnoDB),1000行的表,删除一半再对比OPTIMIZE前后
    • information_schema.TABLES里的DATA_FREE字段 + 真实磁盘文件大小对比
    京ICP备2026067502号-1京公网安备11010502063487号