delete只是标记删除而非物理擦除,innodb设delete_mask位、sql server打页内标记、postgresql标dead tuple,均不归还文件系统空间;真正释放需重建表(如alter table engine=innodb)或专用收缩命令。

DELETE只是标记删除,不真正释放物理空间
MySQL(InnoDB)执行 DELETE 时,并不会立即擦除数据页上的记录,而是把对应行打上“已删除”标记,并加入 purge 链表,由后台 Purge 线程异步清理。这意味着:页内仍保留着空洞,页本身也没被回收或合并。尤其当删除集中在某几个页、或删除后无新数据填充时,这些空洞就长期滞留——形成典型的内部碎片。
- 同一数据页中,删掉 3 行、留下 2 行,页利用率可能从 95% 降到 40%,但页还是那个页,没被重用或压缩
-
DATA_FREE字段在information_schema.TABLES中体现的就是这类未被复用的空闲字节数 - MyISAM 在
DELETE后会立刻复用空闲空间,但 InnoDB 不保证——它优先尝试复用“临近页”的空洞,而非跨页调度
随机 DELETE + 无序主键 = 频繁页分裂与逻辑碎片
如果表用 UUID() 或 NEWID() 作主键,新插入总落在索引中间位置;此时再配合 DELETE(尤其是 WHERE 条件不连续),就会加剧 B+ 树结构失衡:叶节点页分裂后无法填满、兄弟页物理不邻接、逻辑顺序和磁盘顺序脱节——这就是外部碎片的核心成因。
- 查碎片率用:
SELECT avg_fragmentation_in_percent FROM sys.dm_db_index_physical_stats(...)(SQL Server)或DATA_FREE / (DATA_LENGTH + INDEX_LENGTH)(MySQL) - avg_fragmentation_in_percent > 30% 通常意味着扫描需多读 2–3 倍页数,IO 成倍增加
- 即使
DELETE后马上INSERT,若新数据主键不“凑巧”填进旧空洞,碎片依然留存
批量 DELETE 后不做整理,碎片会持续恶化
碎片不是静态问题。一次 DELETE FROM t WHERE created_at 删除百万行,若后续写入节奏慢、或新数据集中在新时间范围(比如只插 2026 年数据),那些老页空洞就彻底“闲置”,而新数据不断向文件末尾追加——表文件(<code>.ibd 或 .mdf)体积膨胀,但有效数据密度下降。
- MySQL 中
OPTIMIZE TABLE会重建表(ALGORITHM=COPY),消除碎片并收缩.ibd文件,但会锁表 - SQL Server 中
ALTER INDEX ... REORGANIZE只整理页内和页间逻辑顺序,不释放空间;REBUILD才能真正回收并重排 - 别依赖自动 purge:它只清标记行,不合并页、不重排聚集索引、不降低
DATA_FREE
SHOW PROCESSLIST 或 sp_who2 往往找不到明显慢 SQL。










