myisam表delete后空间不释放是正常行为,因仅标记删除行供复用,.myd文件大小不变;需执行optimize table重写文件才能真正释放磁盘空间。

MyISAM表DELETE后空间不释放是正常行为,不是故障
MyISAM执行DELETE FROM t WHERE ...后,磁盘上的.MYD文件大小不会变小——它只是把对应行标记为“已删除”,空出的位置留待后续INSERT复用。这点和InnoDB类似,但MyISAM没有聚簇索引、页合并等复杂机制,碎片更直观、也更容易堆积。
OPTIMIZE TABLE是MyISAM碎片整理的首选方案
对MyISAM表,OPTIMIZE TABLE会真正重写.MYD和.MYI文件,物理上移除已删除行占用的空间,并重新排序索引数据。效果立竿见影,且无需额外工具。
- 执行前确认表引擎:
SHOW CREATE TABLE t\G,确保ENGINE=MyISAM - 该命令会加
WRITE LOCK,期间禁止写入,但允许并发读(SELECT) - 需要临时磁盘空间 ≈ 当前
.MYD+.MYI总大小,务必提前检查df -h - 执行后立即生效,
ls -l /var/lib/mysql/db/t.*可验证.MYD文件是否缩小
避免OPTIMIZE TABLE失败的三个硬性前提
跳过这些检查,大概率触发“磁盘满”或“锁等待超时”:
- 磁盘剩余空间 ≥
Data_length + Index_length(查SHOW TABLE STATUS LIKE 't'\G中的这两项) -
innodb_file_per_table设置与此无关(MyISAM不依赖它),但需确认MySQL未启用只读模式:SELECT @@read_only;应为0 - 若在主从架构中操作,先在从库执行:
STOP SLAVE SQL_THREAD;,再运行OPTIMIZE TABLE,完成后START SLAVE;,防止SQL线程卡住
长期高频DELETE场景下,改用ARCHIVE引擎或分区替代
如果某张MyISAM日志表每天删百万行、每周都得OPTIMIZE一次,说明设计已偏离MyISAM适用边界:
- ARCHIVE引擎专为“只插入+偶尔全量读取”优化,DELETE极少、压缩率高,天然规避碎片问题
- 按时间分区不可行(MyISAM不支持原生分区),但可用“表名分片”模拟:如
t_202605、t_202606,旧表直接DROP TABLE,零碎片、秒级完成 - 若必须保留单表结构,考虑迁移到InnoDB并启用
innodb_file_per_table=ON,再配合ALTER TABLE t ENGINE=InnoDB在线重建——虽仍需锁,但比反复OPTIMIZE更可控
Data_free字段始终为0,不能靠它判断碎片程度。唯一可靠方式是对比SHOW TABLE STATUS里的Data_length与实际业务数据量(比如平均行长 × 行数),若前者远大于后者,就是碎片确凿证据。











