delete在innodb中是标记删除而非物理擦除,仅打删除标记并保留数据页,空间可被后续insert/update复用;文件大小不变属正常设计,需通过optimize table或alter table engine=innodb重建表才能真正释放磁盘空间。

DELETE只是标记删除,不是物理擦除
MySQL 的 DELETE 语句在 InnoDB 引擎下,并不会立刻把数据从磁盘上抹掉,而是将对应记录打上“已删除”标记,保留在数据页中。这些空间被标记为“可复用”,后续 INSERT 或 UPDATE 可能直接复用——这是为了减少频繁分配/释放页带来的开销,提升写入性能。
所以你执行 DELETE FROM t WHERE id > 1000 后,du -sh /var/lib/mysql/dbname/t.ibd 看到的文件大小几乎不变,完全正常,不是 bug,是设计使然。
哪些参数和配置影响空间是否可见释放
关键看 innodb_file_per_table 是否开启(默认 5.6+ 已启用):
- 开启时:每张表一个
.ibd文件,空间回收只作用于该文件,但需显式触发重建 - 关闭时:所有表共用
ibdata1,即使删光整库,ibdata1也永远不会变小
另外注意:innodb_page_size(默认 16KB)决定了页内碎片粒度;innodb_fill_factor 影响新页预留空间比例,间接影响后续插入时能否填满旧页空洞。
真正释放磁盘空间的实操方法
必须重建表结构,让引擎把有效记录重新紧凑写入新页,丢弃旧页中的“空洞”。可用以下任一方式:
-
OPTIMIZE TABLE t:最常用,会锁表(5.6+ 对普通 DML 是 online,但 DDL 阶段仍需短暂排他锁) -
ALTER TABLE t ENGINE=InnoDB:效果等同OPTIMIZE,更明确意图,适合脚本化 - MySQL 8.0+ 可用
ALTER TABLE t FORCE, ALGORITHM=INPLACE控制重建方式,但注意ALGORITHM=COPY会锁更久
执行后观察:ls -lh t.ibd 文件大小下降,df -h 中对应磁盘使用率也会更新。但注意:如果表上有长事务未提交,OPTIMIZE 会被阻塞,需先检查 information_schema.INNODB_TRX。
容易忽略的“伪释放”陷阱
有人删完数据顺手 VACUUM ——这是 PostgreSQL 的命令,MySQL 不识别,会报错 ERROR 1064 (42000);也有人误以为 TRUNCATE TABLE 能释放空间,其实它确实会重建表并清空 .ibd,但前提是没外键引用、没正在使用的事务快照,且无法带 WHERE 条件。
最隐蔽的问题是:即使执行了 OPTIMIZE,若所在文件系统是 XFS 且启用了 discard 挂载选项,空间才可能真正归还给 OS;EXT4 默认不自动 discard,得靠 fstrim 或定期运行 innodb_defragment(已废弃)补救。











