MyISAM表DELETE后磁盘空间不释放需执行OPTIMIZE TABLE t——该命令重写.MYD/.MYI文件,物理移除已删行、重建索引并压缩存储;执行前须确认ENGINE=MyISAM、预留足够临时磁盘空间,并注意其加WRITE LOCK阻塞写入但允许并发SELECT。

MyISAM表DELETE后磁盘空间不释放,怎么办?
MyISAM的DELETE FROM t WHERE ...不会缩小.MYD文件,这是设计使然,不是故障。它只标记行可复用,不物理清理。
真正释放空间必须靠OPTIMIZE TABLE t——它会重写.MYD和.MYI文件,移除已删行、重建索引、压缩存储。
- 执行前先确认引擎:
SHOW CREATE TABLE t\G,确保ENGINE=MyISAM -
OPTIMIZE TABLE期间加WRITE LOCK,写入阻塞但SELECT仍可并发执行 - 需要临时磁盘空间 ≈ 当前
.MYD + .MYI总大小,务必提前运行df -h - 主从架构下,应在从库先执行
STOP SLAVE SQL_THREAD;,完成后再START SLAVE;
注意:Data_free字段在MyISAM中恒为0,不能用来判断碎片程度。唯一可靠方式是对比SHOW TABLE STATUS LIKE 't'\G中的Data_length与“平均行长 × 实际行数”,若前者远大于后者,就是碎片确凿证据。
InnoDB表DELETE后索引变慢,怎么验证和修复?
DELETE不删数据页,只打delflag标记,导致B+树页内空洞、填充率下降、统计信息失真——这才是查询变慢的根因,不是索引“坏了”。
别猜,直接查指标:
-
SELECT * FROM information_schema.INNODB_INDEX_STATS WHERE table_name = 't' AND database_name = 'db';,重点看n_leaf_pages是否异常高、n_diff_pfx01是否远低于实际唯一值 -
SHOW INDEX FROM t;,若Cardinality接近0或恒为1,而字段明明有主键/唯一约束,说明统计严重过期 - 执行
ANALYZE TABLE t;后Cardinality无变化?说明采样不足或数据分布极端(如99%为NULL),需强制干预
重建索引不是选“哪个命令更高级”,而是看要动哪一层:
- 仅修单个二级索引退化:用
DROP INDEX idx_name ON t; ADD INDEX idx_name (col); - 聚簇结构已离散(比如主键非自增、大量随机删除):用
ALTER TABLE t FORCE;或ALTER TABLE t ENGINE=InnoDB; - 中小表且想一并更新统计:
OPTIMIZE TABLE t;(MySQL 8.0+支持在线,但仍短暂阻塞DML)
为什么刚OPTIMIZE完又变慢?根本问题不在“修”,而在“删”
频繁重建是补救,不是解法。真正决定碎片程度的,是DELETE怎么写、删多少、删得有多散。
- 禁用
DELETE FROM t WHERE status = 'old'这种一次性百万级操作;改用带范围+LIMIT:DELETE FROM t WHERE id BETWEEN 100000 AND 200000 LIMIT 10000;,每批后加SLEEP(0.1) - 若删除比例 >30%,优先用
CREATE TABLE t_new AS SELECT * FROM t WHERE keep_condition;+RENAME切换,比DELETE+OPTIMIZE更快更干净 - 确保WHERE条件走索引:避免
DATE(create_time)、隐式类型转换、非最左前缀匹配——否则仍是全表扫描+行锁升级 - 检查
innodb_file_per_table是否为ON(影响OPTIMIZE后空间是否真正归还OS)
真正容易被忽略的是:碎片问题从来不是“删完再修”,而是“怎么删”决定碎不碎。监控Data_free / Data_length比值(>20%~30%即预警)、缓冲池读取比(buffer_pool_reads / buffer_pool_read_requests)、以及EXPLAIN FORMAT=JSON里rows_estimation_method和实际返回行数偏差,比等慢查询报警更有价值。



















