7.7 实战:解释订单查询的计划变化
综合实验不追求把某个 node 调成最快,而是建立一条可审计推理链:确定性数据制造已知分布,保存修复前 JSON 计划,只改变一个统计/条件因素,再保存修复后计划并由程序检查关系。
7.7.1 用固定数据种子制造估算偏差
确认 service 指向可写 pg36_shop L1:
all 的顺序:
fixture 分布固定:
region/status 分别四等分但完全对应。普通单列统计近似把两个 25% predicate 相乘,所以两种组合都估约 6250。具体值随 ANALYZE sample 波动,不能 hard-code;raw stats-before-*.json 保存本次 estimate、actual、buffers、settings 和时间。
参数实验选择 128-byte payload,确保 hot tenant 的 heap fetch 与 cold tenant 有明显路径取舍。analyzer 不强制节点必须是 Seq/Index,只强制 custom estimate 接近实际、generic 对两个参数使用同一 estimate,且 hot generic 偏差至少 100 倍。
7.7.2 修复统计与条件表达后重新比较
扩展统计只改变 planner knowledge,不改变数据、index 或 SQL:
这证明问题属于跨列相关,不需要先建组合 index;若查询性能仍不达标,再用第 9 章方法评估 index。扩展统计改善 estimate 也可能不改变 node,因为本查询没有 region/status index 且需要 seq scan;“plan shape 没变”不代表修复无效。
分区实验只改变 predicate:
修复 wrapped 条件应在应用边界先算月初/月末,再直接写 partition-key 半开范围。创建 expression index 可能帮助分区内 filter,却不保证 partition bounds 能由该表达式裁剪;index 与 pruning 是两种机制。
父表统计单独验证 0→4。若只看 child autoanalyze,会遗漏 parent。生产维护频率由分布变化而非固定日历决定,并观察 pg_stat_progress_analyze、锁与资源。
查看结果:
一次实测:
时间与 buffers 用来比较同一次受控实验,不进入稳定 summary。要比较性能,应重复运行并记录 cache/并发条件;本章只验 cardinality 与裁剪机制。
安全复位
实验对象保留供观察。确认不再需要后:
reset 先复核 database、primary、ch04-v1、两个 relation marker 与两个 token,再只删除 ch07 relation。错误 token 必须 exit 3,且对象仍存在;成功后 remaining_ch07_relations=0,ch04-v1 relation checksum 仍为:
7.7.3 把一条有证据的计划规则追加到规约
v0.1 的 PREF-PLAN-005 已声明:
baseline-v0.2-proposal.json 不改写不可变 v0.1,而是基于其 canonical checksum 提案:
新增 runtime check:
Python analyzer 会重新计算 v0.1 canonical checksum,拒绝 proposal 绑定到被悄悄修改的 base;也验证三项 evidence artifact 存在。当前 proposal 仍是 candidate,因为 promotion 还要求:
- PostgreSQL 14–18 兼容矩阵通过;
- 至少一个不同硬件或规模复测;
- 合并为独立、不可变的 v0.2 release artifact。
这体现规则证据的正确节奏:单次 PostgreSQL 18.6 实验足以形成候选 runtime check,不足以宣称跨版本普遍阈值。后续复测即使 node type 不同,只要 cardinality 与 partition pruning 的因果关系仍成立,规则就得到进一步确认;若机制变化,就修正 scope/compatibility,而不是让测试强行 grep 旧节点。
完成本章后,读者应提交的不是一张漂亮计划图,而是:
下一章会从 Pigsty 的真实慢查询时间窗选择 query family,再沿本章方法进入单条计划。
上一节:建立计划证据基线 · 返回本章目录 · 下一章:抽丝剥茧:慢 SQL 诊断方法论 · 查看全书目录 · 查看索引中心