直接用DELETE WHERE time BETWEEN...在大表上会卡死,因未走分区裁剪导致全表扫描、逐行锁及大量undo日志;须确保分区键为time字段本身且类型为DATETIME/TIMESTAMP,WHERE条件用裸列比较。

为什么直接用 DELETE WHERE time BETWEEN ... 在大表上会卡死
因为没走分区裁剪,全表扫描+逐行锁+大量undo日志。哪怕你表按 time 做了Range分区,只要WHERE里用了函数(比如 DATE_SUB(NOW(), INTERVAL 30 DAY))、或参数绑定不明确,优化器就可能放弃分区裁剪,退化成全分区扫描。
实操建议:
- 确认分区键是
time字段本身(不能是DATE(time)或YEAR(time)),且类型是DATETIME/TIMESTAMP - WHERE条件必须是「裸列比较」,例如
time ,而不是 <code>time - 用
EXPLAIN PARTITIONS验证是否只访问目标分区:EXPLAIN PARTITIONS SELECT * FROM logs WHERE time < '2024-01-01';
输出里的partitions列应只显示几个具体分区名,不是all
如何安全批量删掉过期分区(不是删数据,是删整个分区)
比 DELETE 快几个数量级,因为不走DML流程,不写redo/undo,也不触发触发器和外键检查。但前提是你的过期数据恰好落在独立分区里。
实操建议:
- 建表时就按天/周/月对齐分区,例如每月一个分区:
PARTITION BY RANGE (TO_DAYS(time)) ( PARTITION p202312 VALUES LESS THAN (TO_DAYS('2024-01-01')), PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')), PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')) ); - 删除旧分区用
ALTER TABLE ... DROP PARTITION:ALTER TABLE logs DROP PARTITION p202312;
- 注意:MySQL 8.0+ 支持
DROP PARTITION不阻塞DML(仅短暂元数据锁),但5.7会锁整表;执行前先查INFORMATION_SCHEMA.PARTITIONS确认分区名
想保留部分历史数据,又不想手动维护分区边界怎么办
靠 REORGANIZE PARTITION 动态收缩分区范围,避免把“要留的数据”误删。典型场景:保留最近90天,每天自动归档,但某天发现还要查100天前的某条记录。
实操建议:
- 不要直接
DROP最老分区,先用REORGANIZE把它拆成两块:ALTER TABLE logs REORGANIZE PARTITION p202312 INTO ( PARTITION p202312_keep VALUES LESS THAN (TO_DAYS('2024-01-05')), PARTITION p202312_drop VALUES LESS THAN (TO_DAYS('2024-01-01')) ); - 再单独删掉
p202312_drop,保留p202312_keep - 注意:REORGANIZE 要求源分区和目标分区的值域必须连续且无重叠,否则报错
ERROR 1486 (HY000): Constant, random or timezone-dependent expressions in (sub)partitioning function are not allowed
分区后查询变慢?可能是统计信息没更新
MySQL不会在每次 DROP PARTITION 后自动更新全局统计信息,导致优化器误判行数,选错索引甚至放弃分区裁剪。
实操建议:
- 删完分区立刻执行
ANALYZE TABLE logs;,强制刷新统计信息 - 如果表特别大,可加
WITH SYNC(MySQL 8.0.23+)避免后台异步延迟:ANALYZE TABLE logs WITH SYNC;
- 验证是否生效:查
INFORMATION_SCHEMA.STATISTICS中TABLE_ROWS是否明显减少,或再次跑EXPLAIN PARTITIONS
分区裁剪不是开箱即用的功能,它极度依赖SQL写法、分区定义和统计信息三者严格对齐。少一个环节,就退回全表扫描。

















