optimize table 未释放空间的根本原因是表未启用独立表空间(innodb_file_per_table=off),导致其数据存于共享表空间 ibdata1 中,而该文件永不缩容;仅当 innodb_file_per_table=on 时,optimize 才会重建 .ibd 文件并释放磁盘空间。

OPTIMIZE TABLE 为什么没释放空间?先查表空间模式
根本原因往往不是命令用错了,而是表根本不在能释放空间的路径上。InnoDB 表只有启用 innodb_file_per_table=ON 时,OPTIMIZE TABLE 才会生成新 .ibd 文件并替换旧文件;否则所有表都挤在共享表空间 ibdata1 里,这个文件永远不缩容。
执行这两条命令确认现状:
SELECT @@innodb_file_per_table;
SELECT TABLE_NAME, CREATE_OPTIONS FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table';
如果返回 innodb_file_per_table 是 0,或 CREATE_OPTIONS 为空/不含 ROW_FORMAT,那 OPTIMIZE TABLE 对磁盘空间毫无作用——它只在 ibdata1 内部整理页,不删文件。
TRUNCATE 后 .ibd 文件大小不变?必须跟 OPTIMIZE 或 ALTER
InnoDB 的 TRUNCATE TABLE 只清空数据页、重置 AUTO_INCREMENT,但不会收缩 .ibd 文件。你用 du -sh *.ibd 看大小纹丝不动,是正常行为,不是 bug。
真正释放磁盘空间的操作链只有一条:
- 确认表是独立表空间(
innodb_file_per_table=1) - 执行
TRUNCATE TABLE your_table - 立刻执行
OPTIMIZE TABLE your_table或等价的ALTER TABLE your_table ENGINE=InnoDB
注意:TRUNCATE 是 DDL,隐式提交、不可回滚;OPTIMIZE 过程中表会被锁(8.0+ 允许部分并发读,但写仍阻塞),别在高峰期跑。
DELETE 大量数据后空间不回收?别直接 OPTIMIZE
DELETE FROM logs WHERE created_at 只打删除标记,不归还页给文件系统。此时 <code>DATA_FREE 会变大,但 .ibd 文件体积不变——InnoDB 把这些空页留着供后续插入复用。
如果目标是清空整张表,DELETE + OPTIMIZE 比 TRUNCATE + OPTIMIZE 更慢、更占临时空间:前者要搬运所有“逻辑已删但物理还在”的记录,后者从零开始重建。
真正该做的判断是:
- 需要保留部分数据 → 分批
DELETE+pt-osc在线重建(避免锁表) - 整表清空 → 直接
TRUNCATE+OPTIMIZE - 表太大不敢锁 → 改用分区表,按时间 DROP PARTITION,空间立即释放
索引比数据还大?先砍 TEXT/VARCHAR 上的普通索引
二级索引存储的是「索引列值 + 主键值」。在 VARCHAR(2000) 或 TEXT 字段上建普通索引,单条索引记录可能达几 KB,索引体积轻松碾压数据本身。
检查方式:
SELECT TABLE_NAME, INDEX_NAME, COLUMN_NAME, SEQ_IN_INDEX FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA = 'your_db' AND TABLE_NAME = 'your_table' ORDER BY SEQ_IN_INDEX;
重点关注那些指向长文本字段的单列索引。删除它们的命令是:
ALTER TABLE your_table DROP INDEX idx_long_text_field;
如果业务真需要查这个字段,改用前缀索引(如 INDEX idx_prefix (content(100)))或全文索引,而非全字段索引。
最后提醒一句:OPTIMIZE TABLE 和 ALTER TABLE ... ENGINE=InnoDB 都会重建所有索引。如果你刚删掉一个巨型索引,再跑一遍 OPTIMIZE 就白花了 IO——空间已经省下来了,不用额外操作。











