DELETE不释放物理空间是主流数据库默认行为,因仅逻辑标记删除(如InnoDB设delete_mask、SQL Server打页内标记、PostgreSQL标dead tuple),不归还文件系统空间;真正释放需重建表(如ALTER TABLE ENGINE=InnoDB)或专用收缩命令。

DELETE 不会释放物理空间,这是所有主流数据库的默认行为,不是 bug,也不是你写错了语句。
DELETE 只是逻辑标记,不是物理擦除
InnoDB 把行头的 delete_mask 位设为 1;SQL Server 在页内打“已删除”标记;PostgreSQL 标记为 dead tuple;Oracle 将行置为“可重用”但不降高水位线(HWM)。这些操作都不移动页、不归还文件系统空间,.ibd、.mdf 或数据文件尺寸完全不变。
典型现象包括:
-
SHOW TABLE STATUS中Data_length几乎不变,Data_free反而升高 -
du -sh table.ibd或sys.dm_db_file_space_usage显示未释放 - 即使
SELECT COUNT(*) = 0,文件大小纹丝不动
真正释放空间必须重建或收缩
想让磁盘文件变小,必须绕过“标记删除”机制,走重建路径:
- MySQL:优先用
ALTER TABLE t ENGINE=InnoDB(语义清晰、RDS 多数支持 Online DDL);慎用OPTIMIZE TABLE t,它等价于ALTER TABLE t FORCE,部分版本锁更重 - SQL Server:堆表加
TABLOCK后DELETE可能释放页;聚集表一律用ALTER INDEX ALL ON t REBUILD;DBCC SHRINKFILE是最后手段,只对目标文件生效,且必然制造索引碎片 - PostgreSQL:运行
VACUUM FULL t(阻塞读写)或pg_repack(在线);VACUUM默认只回收空间供内部复用,不返还 OS - Oracle:用
ALTER TABLE t SHRINK SPACE(需启用行移动),或TRUNCATE(不可回滚、重置 HWM)
执行前不检查这三件事,大概率失败
盲目触发重建命令,轻则锁表数小时,重则写满磁盘、阻塞全库:
-
innodb_file_per_table = ON(MySQL)或确认目标表在独立文件组(SQL Server),否则重建无效 - 磁盘剩余空间 ≥ 当前
Data_length× 1.5(临时表 + redo/binlog 日志要落盘) - 无长事务阻塞 purge/vacuum:MySQL 查
information_schema.INNODB_TRX,PostgreSQL 查pg_stat_activity中state = 'idle in transaction'且backend_start超 10 分钟的连接
最常被忽略的是托管服务限制:RDS、PolarDB、Azure SQL 等常禁用 OPTIMIZE TABLE 或自动转到只读副本执行,得先查控制台文档。另外,TRUNCATE 和 DROP 虽能立刻释放,但不支持 WHERE 条件,且会重置自增、破坏外键依赖——别当成 DELETE 的替代品乱用。

















