DELETE不释放磁盘空间是设计行为而非bug;MySQL需OPTIMIZE TABLE(InnoDB要求innodb_file_per_table=ON)或ALTER TABLE ENGINE=InnoDB重建表,SQL Server则需ALTER INDEX REBUILD/REORGANIZE整理索引碎片。

DELETE 操作本身不释放磁盘空间,这是设计行为,不是 bug。真正要回收空间,得靠后续的碎片整理动作——但具体怎么做,取决于你用的是 MySQL 还是 SQL Server,引擎是 MyISAM 还是 InnoDB,甚至是否在主从架构里。
MySQL 中 OPTIMIZE TABLE 是最直接有效的方案
对 MyISAM 和 InnoDB 表都适用,本质是重建表结构 + 索引文件,物理上抹掉已标记删除的行:
-
OPTIMIZE TABLE t会加 WRITE LOCK,期间INSERT/UPDATE被阻塞,但SELECT仍可并发执行 - 执行前必须确认磁盘剩余空间 ≥
Data_length + Index_length(查SHOW TABLE STATUS LIKE 't'\G) - MyISAM 表执行后
.MYD文件大小立刻变小;InnoDB 表则依赖innodb_file_per_table=ON才能真正缩表空间 -
Data_free字段在 MyISAM 中恒为 0,不能用来判断碎片——唯一靠谱方式是比对Data_length和 “平均行长 × 实际行数”
SQL Server 用 ALTER INDEX REORGANIZE 或 REBUILD
它不操作表本身,而是针对索引做维护,因为碎片主要存在于索引页中:
- 碎片率 10%–30%:用
ALTER INDEX ALL ON [schema].[table] REORGANIZE,在线、低开销、不锁表 - 碎片率 >30%:用
ALTER INDEX ALL ON [schema].[table] REBUILD WITH (ONLINE = ON),效果彻底但需额外日志空间,且 SQL Server Standard 版不支持ONLINE = ON - 查询碎片率必须走
sys.dm_db_index_physical_stats,不能只看DBCC SHOWCONTIG(已过时) - 重建索引会产生大量事务日志,如果数据库启用了完整恢复模式,记得及时备份日志,否则
log_full可能卡住操作
别踩这些坑:MyISAM 和 InnoDB 的关键差异
同一句 DELETE FROM t WHERE ... 在两种引擎下表现完全不同,整理逻辑也得跟着变:
- MyISAM:
DELETE FROM t(无 WHERE)会立刻清空.MYD文件;但带条件的DELETE绝对不释放空间,必须OPTIMIZE TABLE - InnoDB:
DELETE后空间由 Purge 线程异步回收,OPTIMIZE TABLE实际等价于ALTER TABLE t ENGINE=InnoDB,会重建整张表 - InnoDB 表若
innodb_file_per_table=OFF,OPTIMIZE TABLE不会缩小ibdata1,只会把空闲页归还给表空间内部——这种情况下,想真正缩磁盘,只能导出+重建+导入 - MyISAM 不支持分区,所谓“按月分表”只能靠命名约定(如
t_202606),旧表直接DROP TABLE,比反复OPTIMIZE更干净
Data_free 或“删了 50% 行”,而要看实际 I/O 压力和磁盘水位。一张 10GB 的表,Data_free 占 2GB,可能毫无感知;但一张 200GB 的慢查询表,哪怕只有 5% 碎片,也可能让全表扫描多扫几十万页。

















