依据分区键把整块分区从查询中排除

分区裁剪跳过查询不可能用到的整张分区

分区裁剪排除的是整张不可能包含匹配行的分区。它与在剩余分区内部利用索引寻找行不同,也不是按页工作的缓存技巧。PostgreSQL 可以在查询规划阶段完成排除,也可以等参数值在执行过程中可用后再裁剪。要判断它是否生效,需要查看实际访问的分区,而不是只看最终返回了多少行。 证明哪些分区不可能命中 声明式分区的边界来自分区键。把日期键限制在十月,可以证明九月和十一月与查询无关。裁剪依赖这些边界,而不是估计其他分区“可能很少匹配”。统计信息仍影响其他规划决策,但行数估计不准,不会让分区归属变成概率判断;这两个层面的信息应分开解释。 裁剪不要求先建索引。排除无关分区后,如果剩余分区的大部分行都需要读取,PostgreSQL 完全可能选择顺序扫描。所以,出现 Index Scan 并不是裁剪成功的判据。应先检查物理关系名和计划结构,再单独评价剩余每张表内部的访问方式。顺序扫描一张相关分区,仍然可能已经省掉其他分区的大量工作。 条件必须能对应分区键及其比较语义。把日期列转换成文本,可能隐藏直接的边界关系,即使某个字符串条件恰好仍选出相同行。应使用有类型的参数和直接表达目标区间的条件,不能假定分区键外面套任意表达式都保留裁剪能力,甚至不能默认它保留原来的日期语义。 参数也能在执行时触发裁剪 常量可以支持规划期裁剪,使计划只留下相关子表。预备语句的通用计划无法提前知道未来每次传入的参数值,但这不表示它必须扫描全部分区:执行初始化时已经知道的值,也可以用于执行期裁剪。看见通用计划中原本存在多个分支,还不足以判断最终都会读取。 EXPLAIN ANALYZE 用 Subplans Removed 报告初始化阶段排除的分区。另一些情况,例如嵌套循环内侧参数在执行期间变化,可以在实际执行过程中继续裁剪。分析这些场景时,需要查看循环次数和子计划是否运行。某个节点显示从未执行,也可能是外层没有输入等其他原因,不能脱离完整计划单独当成裁剪证据。 因此,不能把这些规则简化成“只要不是常量表达式,就会失去裁剪”。参数何时可用,以及表达式是否受支持,共同决定何时能够排除分区。定制计划和通用计划可能不同,但返回同样答案。真正值得确认的是:负载使用真实参数时,哪些分区实际被访问了。 坏掉的样子 只过滤无关字段,可能让所有分区仍然相关;把键转换成无法对应边界的表达式,也可能产生同样结果。调整分区布局前,应检查准确条件和 enable_partition_pruning 设置。手工添加相互矛盾的 CHECK 约束并不是修复方法;普通本地声明式分区会执行自己的边界规则,而基于 CHECK 的约束排除是另一种优化机制。 裁剪也不保证没有锁或规划成本可以忽略。执行初始化阶段排除的分区,在执行开始时仍然会被锁定。大型分区层级可能消耗规划时间和会话内的元数据内存,即使最后只扫描少数子表。应根据真实查询和维护模式选择层级,而不是追求尽可能多的分区数量。 最后,即使裁掉两个分区,第三个分区内部仍可能有几千条无关行。合适的局部索引可能帮助第二阶段。最终返回一百行,不证明只检查了一百行;缓冲命中也不等于物理磁盘读取。必须沿着计划节点看清扫描、过滤与输出之间的区别。 快速验证 把下面内容保存为 partition-pruning.sql,在可丢弃的 PostgreSQL 18 数据库中执行 psql -X -v ON_ERROR_STOP=1 -f partition-pruning.sql。脚本在事务内启用裁剪,并用通用预备计划展示初始化阶段的行为。临时分区上没有建立任何索引。 \set ON_ERROR_STOP on BEGIN; SET LOCAL datestyle='ISO, YMD'; SET LOCAL enable_partition_pruning=on; CREATE TEMP TABLE observations (day date,id integer) PARTITION BY RANGE(day); CREATE TEMP TABLE obs_sep PARTITION OF observations FOR VALUES FROM ('2026-09-01') TO ('2026-10-01'); CREATE TEMP TABLE obs_oct PARTITION OF observations FOR VALUES FROM ('2026-10-01') TO ('2026-11-01'); CREATE TEMP TABLE obs_nov PARTITION OF observations FOR VALUES FROM ('2026-11-01') TO ('2026-12-01'); INSERT INTO observations SELECT DATE '2026-09-01'+g%90,g FROM generate_series(0,8999) AS g; ANALYZE observations; EXPLAIN (ANALYZE, BUFFERS, TIMING OFF) SELECT count(*) FROM observations WHERE day>=DATE '2026-10-01' AND day<DATE '2026-11-01'; SET LOCAL plan_cache_mode=force_generic_plan; PREPARE by_day(date) AS SELECT count(*) FROM observations WHERE day=$1; EXPLAIN (ANALYZE, BUFFERS, TIMING OFF) EXECUTE by_day(DATE '2026-10-10'); EXECUTE by_day(DATE '2026-10-10'); EXPLAIN (ANALYZE, BUFFERS, TIMING OFF) SELECT count(*) FROM observations WHERE day::text='2026-10-10'; DO $$ BEGIN IF (SELECT count(*) FROM observations WHERE day=DATE '2026-10-10') <> 100 OR (SELECT count(*) FROM observations WHERE day>=DATE '2026-10-01' AND day<DATE '2026-11-01') <> 3100 THEN RAISE EXCEPTION 'unexpected pruned result'; END IF; END $$; DEALLOCATE by_day; ROLLBACK; PostgreSQL 18.6 把常量十月区间规划为只扫描 obs_oct,读取三千一百条匹配行。通用日期查询报告 Subplans Removed 为二,返回一百行,但仍需在十月分区中过滤三千条其他记录。这同时展示了成功排除无关分区,以及相关分区内部尚未消除的工作量。 ...

二月 13, 2028 · 2 分钟 · XB Field Notes
一张逻辑表,若干物理分片。

表分区按范围或键拆分数据

范围分区把边界定义变成写入路由规则。PostgreSQL 按键值把行放到能够接受它的分区,无论这条数据准时到达,还是晚了几个月。任何查询获得裁剪收益之前,都需要先定义正确边界、使用匹配的时间类型,并安排缺失范围的处理流程。分区建表成功,只是这些条件中的一部分。 使用相邻的左闭右开范围 范围分区包含下界,不包含上界。九月可以从九月一日开始,一直到十月一日之前;十月正好从同一边界开始,双方不会重叠。不要靠猜测一个月最后可表示的时间戳来定义范围。相邻的左闭右开区间,不依赖时间字段究竟保留多少位小数,也能避免边界遗漏。 查询同样需要准确表达区间。大于等于下界、小于上界的条件,适合描述一个月的数据。BETWEEN 包含两个端点,因此,把下月第一瞬间写成其上界,可能把下个月的数据也查进来。分区路由只能执行数据布局规则,不能替一个请求了错误区间的条件修正业务含义。 分区键类型与时区也是定义的一部分。timestamptz 代表瞬间,不带时区的 timestamp 则代表没有对应时区解释的本地日期时间。示例为每个边界显式写出 UTC 偏移。如果业务月份按另一个时区划分,应根据那个时区的规则推导边界,而不是假定所有本地日期都对应相同的实际时长。 缺失范围是一种运维状态 没有分区接受新键值时,插入会报错,除非 DEFAULT 分区能够接住它。日历进入新月份,并不会让 PostgreSQL 自动创建下一张月表。应在写入需要之前创建范围,同时考虑迟到数据和旧日期修正。输入键不必单调增长,路由照样能够工作;关键是相应目标范围仍然存在。 DEFAULT 可以接收显式范围之外的值,减少立即发生的插入失败,但如果不检查默认表,也会掩盖分区管理遗漏。持续增长的默认分区可能混入多个互不相关的时间范围,让后续整理更困难。应让“有意保留的例外数据”与“忘记创建分区的日常数据”保持可辨认,不能把插入暂时不报错当成管理正常。 新增显式分区,会改变默认分区能够接受的值。如果新范围对应的行还留在 DEFAULT 中,PostgreSQL 会拒绝变更,而不会允许两处同时拥有这批数据。应先把这些行迁移到合适的暂存表或按规则处理,再挂载新分区。边界是被执行的数据规则,不是可以随意标错的说明标签。 坏掉的样子 下一月分区未创建时,部署可能恰好在跨月瞬间出现写入失败。补建分区又可能因为 DEFAULT 已经积累对应行而失败。这两种问题都属于路由和数据位置,不是规划器统计陈旧。应先检查被拒绝的键和当前边界,再决定操作,不能直接运行 ANALYZE 或改索引,希望它们修复缺失范围。 ATTACH 可能扫描候选表以验证边界,也可能检查默认分区中是否存在冲突数据。合适且已验证的约束能省去部分扫描,但操作仍需要锁,并要求表结构兼容。活跃表上迁移数据时,需要协调写入者和锁获取;一个隔离环境中的事务示例,不是完整的在线迁移操作方案。 是否允许空值,与范围选择是两回事。示例要求时间戳非空,避免缺失事件时间悄悄变成普通的例外数据。迁移后还应核对行数和标识;ATTACH 成功不能证明更早的复制步骤没有丢行或重复,数据核对不能省略。 快速验证 把下面内容保存为 partition-ranges.sql,在可丢弃的 PostgreSQL 18 数据库中执行 psql -X -v ON_ERROR_STOP=1 -f partition-ranges.sql。全部表都是临时表,整个实验最后回滚清除。 ...

二月 10, 2028 · 2 分钟 · XB Field Notes