跳转到主内容
极星编程网:以代码为星,赴技术山海!

为什么MySQL 5.7在大量Delete后空间不释放_执行optimize table收缩碎片

DELETE后磁盘空间不释放是InnoDB正常设计行为,因仅标记删除而不物理回收;OPTIMIZE TABLE可收缩.ibd文件,但须满足innodb_file_per_table=ON、无长事务、磁盘空间充足三个前提。 DELETE 后磁盘空间不释放,是 InnoDB 的设计行为,不是故障;OPTIMIZE TABLE 能收缩 .ibd 文件,但必须满足三个硬性前提,否则会卡住、锁死或写满磁盘。 为什么 DELETE 不释放空间 InnoDB 的 DELETE 只是把行标记为“可复用”,并不归还给操作系统。数据文件(如
test.ibd
)大小完全不变,
SHOW TABLE STATUS
中的
Data_length
和
du -sh *.ibd
都不会下降。这不是配置错,也不是 bug——它是 MVCC 和 purge 机制共同决定的:未提交的长事务会阻塞 purge 线程,已删记录就一直占着页。 常见错误现象包括:
SELECT COUNT(*)
返回 0,但
Data_free
反而变大(碎片加剧);
df -h
看不到磁盘释放;
TRUNCATE TABLE
能立刻缩容,但
DELETE FROM t WHERE ...
完全无效。 OPTIMIZE TABLE 在 MySQL 5.7 上的实际行为 MySQL 5.7 的
OPTIMIZE TABLE
本质是
ALTER TABLE t ENGINE=InnoDB
,它重建整张表:读出所有有效行 → 写入新页 → 替换原
.ibd
文件。因此它能真正释放空间,但代价明确: MySQL(Linux) MySQL 9.6.0是面向Linux平台的2026年创新版本,核心架构迎来重大革新。其将外键约束与级联操作从InnoDB引擎层上移至SQL层,确保所有数据变更均被完整记录至Binlog,彻底解决了CDC(变更数据捕获)与主从复制中的数据不一致难题。此外,该版本引入container_aware启动选项以原生适配容器环境,并对审计日志进行了组件化重构,为追求极致数据一致性与云原生体验的开发者提供了全新选择。 下载 全程加
MDL_EXCLUSIVE
锁,所有 DML 和大部分 SELECT 都被阻塞 需要额外磁盘空间 ≥ 当前表的
Data_length
(临时表阶段写满就报
ERROR 1034
) 不走 binlog(ROW 格式下),从库不会同步该操作,主从
.ibd
大小会不一致 即使
innodb_file_per_table=ON
,若表本身已很紧凑(比如刚 INSERT 就 DELETE),
OPTIMIZE
后
.ibd
大小也可能不变 执行前必须检查的三件事 跳过任意一项,
OPTIMIZE TABLE
就可能卡死或引发事故: 查磁盘余量:
df -h /var/lib/mysql
,确认剩余空间 >
SHOW TABLE STATUS LIKE 't'\G
中的
Data_length
查长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60;
,有结果就得先 kill 确认引擎和独立表空间:
SHOW CREATE TABLE t\G
必须含
ENGINE=InnoDB
且实例已启用
innodb_file_per_table=ON
替代方案:什么情况下不该用 OPTIMIZE TABLE 当表超过 50GB 或磁盘使用率 >85% 时,
OPTIMIZE TABLE
风险极高。更稳妥的做法是: 整表清空且允许停机:直接
TRUNCATE TABLE t
(注意外键约束会报
ERROR 1701
) 只保留近期数据:用
mysqldump -u root -p --where="create_time > '2025-01-01'" db t > t_recent.sql
导出,再
DROP
+
CREATE
+
source
线上大表无法停机:改用
pt-online-schema-change --alter="ENGINE=InnoDB" --execute
,但它仍需双倍磁盘空间,且不能在从库上运行 最易被忽略的一点:
OPTIMIZE TABLE
不是“越勤快越好”。频繁执行反而会因临时表开销增大
Data_free
,让碎片问题更隐蔽。重点应放在预防——比如避免超长事务、按月分区归档、监控
Data_free / Data_length
比值是否持续 > 25%。

相关文章