分区表删除比DELETE快的根本原因是直接删除整个分区文件,不扫描行、不写undo log和逐行binlog;而DELETE需逐行标记、加锁、写日志并后续VACUUM,性能开销高。

分区表删除比DELETE快的根本原因
直接执行 DELETE FROM logs WHERE log_time 会触发全表扫描、生成大量 undo log、持有行锁甚至升级为表锁;而 <code>ALTER TABLE logs DROP PARTITION p2024 是元数据操作,不走存储引擎层,不写 undo/redo,不锁数据行,秒级完成。
按时间建 RANGE 分区时必须避开的陷阱
用 YEAR(log_time) 或 TO_DAYS(log_time) 分区看似简单,但容易踩坑:
- 分区字段不能是表达式结果(如
DATE(log_time)),必须是确定性函数,YEAR()和TO_DAYS()可用,NOW()、CURDATE()不可用 - 分区边界值必须严格递增且连续,漏掉某年会导致插入失败(例如只有
p2024和p2026,2025 年数据无法写入) - 分区列上必须有索引(或为主键一部分),否则基于该列的查询可能仍扫全表
- MySQL 8.0+ 支持
EXCHANGE PARTITION,但要求源表和目标表结构、字符集、ROW_FORMAT 完全一致,否则报错ERROR 1731 (HY000): Cannot exchange a visible partition with table
备份旧分区数据的两种安全做法
删除前想保留一份冷备?别用 mysqldump 全量导出——它会锁表、慢、且无法只导一个分区。推荐:
- 用
SELECT ... INTO OUTFILE导出单分区数据(需确保secure_file_priv允许路径):SELECT * FROM logs PARTITION (p2024) INTO OUTFILE '/backup/logs_2024.csv' FIELDS TERMINATED BY ','; - 用
mysqldump --where="log_time >= '2024-01-01' AND log_time 模拟分区导出,但注意:该方式仍会全表扫描,仅适合已有索引且数据量不大的场景 - 更稳妥的方案是提前建好归档库,用
INSERT INTO archive.logs_2024 SELECT * FROM logs PARTITION (p2024);——只要目标表结构兼容,这条语句不锁原表主键,且可加LIMIT控制内存使用
清理后空间没释放?这是最常被忽略的一步
DROP PARTITION 后,磁盘空间不会立刻还给操作系统,尤其使用 innodb_file_per_table = OFF 时,数据会留在 ibdata1 中。必须手动执行:
- 确认已开启独立表空间:
SHOW VARIABLES LIKE 'innodb_file_per_table';(值必须为ON) - 对分区表执行
OPTIMIZE TABLE logs;——它会重建整个表,释放碎片并回收空间,但会短暂锁表 - 若无法锁表,改用在线 DDL:
ALTER TABLE logs ENGINE=InnoDB, ALGORITHM=INPLACE, LOCK=NONE;(MySQL 5.6+ 支持,但要求无全文索引、无虚拟列等限制)
真正难的不是删数据,而是删完之后没人检查 information_schema.TABLES 里的 data_length 是否下降,以及磁盘 df -h 是否同步释放——这两步漏掉,等于白删。


















