RANGE分区加速UPDATE的根本原因是分区裁剪使数据库跳过无关数据页。当WHERE条件含分区键的等值或范围查询且未被函数包裹时,系统仅扫描目标分区,大幅减少I/O;否则无法裁剪,性能无提升。
根本原因不是分区本身快,而是它让 UPDATE 能跳过大量无关数据页。当 WHERE 条件命中分区键(比如 WHERE created_at >= '2025-01-01'),PostgreSQL 或 MySQL 会自动裁剪(prune)掉不满足范围的分区,只扫描目标分区内的数据——物理上少读几十 GB,自然快得多。
但前提是:UPDATE 的 WHERE 必须包含分区键的等值或范围条件,且不能被函数包裹(比如 WHERE DATE(created_at) = '2025-01-01' 就无法裁剪)。
RANGE 分区不像 HASH 那样“自动分”,你得自己定义每个分区覆盖哪一段值。常见错误是边界写错导致数据写入失败或跨区。
CREATE TABLE logs (id BIGINT, created_at DATETIME) PARTITION BY RANGE (TO_DAYS(created_at)) (PARTITION p2025 VALUES LESS THAN (TO_DAYS('2025-01-01')), PARTITION p2025 VALUES LESS THAN (TO_DAYS('2025-01-01')));
CREATE TABLE logs (id BIGINT, created_at DATE) PARTITION BY RANGE (created_at); CREATE TABLE logs_2025 PARTITION OF logs FOR VALUES FROM ('2025-01-01') TO ('2025-01-01');
no partition for partitioning key
很多人以为加了分区就自动优化,结果 EXPLAIN 一看还是全分区扫描。关键看输出里有没有 Partition Filter(PostgreSQL)或 partitions: p2025(MySQL)。

DATE 类型,就别传 '2025-01-01 12:00:00' 字符串WHERE YEAR(created_at) = 2025 → 改成 WHERE created_at >= '2025-01-01' AND created_at
WHERE created_at > ? AND status = 'pending',而 status 没建索引,仍可能触发全分区扫描旧分区不会自动删除,长期运行后会出现几百个分区,导致查询计划生成变慢、DDL 操作卡顿。DROP 分区虽快,但需手动调度,且不能影响正在执行的 UPDATE。
ALTER TABLE logs DROP PARTITION p2025,但注意该分区所有数据立即丢失,不可回滚DETACH PARTITION logs_2025,再 DROP TABLE logs_2025,更安全但步骤多REORGANIZE PARTITION 或新增分区,这类操作会锁整个父表最易被忽略的一点:分区键一旦选错(比如用 user_id 而非时间),后续几乎无法通过 UPDATE 语句受益——因为业务更新通常按状态或 ID,而非按用户哈希范围。