# 参数、缓存计划与计划漂移

LLMS 索引： [llms.txt](/llms.txt)

---

同一 query shape 可能面对完全不同参数选择率。每次用具体值规划可得到针对性路径，却支付 planning 成本；复用 generic plan 可省 planning，却看不到具体参数。缓存计划是成本取舍，不是“prepared statement 必然更快”。

## 7.5.1 自定义计划与通用计划 {#item-7-5-1}

custom plan 把本次参数代入后规划，可使用 MCV、partition bounds 等具体信息；generic plan 保留 `$1`，适合参数分布相近或 planning 昂贵的重复语句。

在 `plan_cache_mode=auto` 下，带参数的 prepared statement 前五次使用 custom plan；之后服务器生成 generic plan，并把它的估算成本与前五次 custom plan 的平均估算成本比较。只有 generic plan 并未贵到值得反复重新规划时，后续执行才会复用它。这个启发式使用规划器成本，不等于实际执行时延；因此还要用代表性参数分别测量 planning time、execution time 与结果行数。`force_custom_plan` / `force_generic_plan` 只适合诊断对照，不应在缺少 workload 证据时全局强制。

prepared statement 是 session 对象。DDL、统计变化和影响解析/规划的设置可触发 re-plan；search_path 变化也会重新解析。transaction pooling 下 client 与 server session 生命周期不同，driver/PgBouncer 的 prepared statement 支持与配置必须单独验证，不能从裸 PostgreSQL session 外推。

## 7.5.2 预备语句、数据倾斜与参数敏感 {#item-7-5-2}

fixture：

```text
tenant 1    = 90000 rows
tenant 2..1001 = 10 rows each
index(tenant_id)
```

强制 custom：

```text
hot:  estimate=90000 actual=90000 → 实测 Seq Scan
cold: estimate=10    actual=10    → 实测 Index Scan
```

强制 generic：

```text
estimate=100 for both
hot actual=90000
cold actual=10
```

具体 node type 受表宽、cache、cost constants 与版本影响，不作为硬断言；真正的参数敏感证据是 custom estimate 能区分 90000/10，而 generic 必须使用同一估计。hot generic 路径即使本次仍足够快，也已经暴露风险：平均延迟可能掩盖少数大 tenant 的尾延迟。

处理方案按证据选择：

- 保持 auto，并观察 generic/custom 计数与参数分布；
- 对真正参数敏感的 workload 分 query shape 或显式选择策略；
- 改 schema/index/partition，使不同选择率下都可接受；
- 对 hot tenant 单独路由/批处理；
- 局部 force custom，并量化 planning 开销。

不要把 tenant literal 拼进 SQL 来“骗 custom plan”；这会扩大 query shape、失去安全参数绑定并污染统计。

## 7.5.3 计划变化是症状，不自动等于回归 {#item-7-5-3}

计划会因以下因素合理变化：

```text
数据量/分布与 ANALYZE sample
参数值与 custom/generic 决策
index/constraint/partition/schema
planner GUC/cost/JIT/parallelism
PostgreSQL/extension 版本
relation page/visibility/correlation
```

回归判断顺序：

1. result contract 是否仍正确；
2. 同 workload 的 latency/throughput/resource/SLO 是否变差；
3. wait 还是 executor 在耗时；
4. estimate/actual 从哪里开始偏；
5. 数据、statistics、settings 与参数是否可比；
6. 新 plan 是否只是节点不同但成本更好；
7. 改动是否增加写放大、WAL、memory 或尾延迟。

对计划做 canonical fingerprint 可以帮助发现变化，但不能把完整 JSON bytes 当稳定 API；版本会新增字段，cost/time 天生动态。保留原始计划，同时抽取 query identity、node responsibilities、cardinality error、buffers/spill/WAL 与环境 facts。

应急时可以用 planner GUC 证明替代路径，长期方案仍要回到统计、SQL、index、参数策略或版本 bug。第 8 章会把这里的计划证据放进慢查询闭环，第 9 章再审查 index 收益和写成本。

---

[上一节：分区裁剪的两种时机](../04/) · [返回本章目录](../) · [下一节：建立计划证据基线](../06/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
