27.7 实战:只调一个已证实的瓶颈
本节从第 26 章继续,不新造一个“参数一改、TPS 翻倍”的玩具例子。
第 26 章已经证明:
可确认的是:
- c8 进入较高 server CPU/queue 区域;
- load generator 没有先饱和;
- 并发提高的 tail 代价很大。
不可确认的是:
- CPU 花在 planning 还是 execution;
- I/O、WAL 或 lock 是否已经限制 throughput;
- 哪个 GUC 能改善;
- c8 是否是 knee。
严格说,第 26 章只证实了 service-center pressure,没有证实 parameter-specific root cause。所以本节的正确目标不是“必须调成”,而是只允许 一个可逆参数假设进入 A/B,并接受拒绝。
27.7.1 从 ch26 证据提出参数假设
候选矩阵
| candidate | ch26 支持 | 缺口 | 决定 |
|---|---|---|---|
raise work_mem |
无 | 六个 cell temp=0 | 不测试 |
raise shared_buffers |
L 有 block read | iowait 低、OS/device 未归因 | 不测试 |
raise max_connections |
无 | 只测 8 clients,knee 未知 | 拒绝 |
synchronous_commit=off |
写产生 WAL | WAL flush 未证实 | 拒绝 durability change |
change wal_compression |
有 WAL/tx | WAL I/O 未证实、CPU 已高 | 不测试 |
| force generic plan | prepared + CPU pressure | planning time未测,auto 可能已 generic | 允许证伪 |
这张表的关键不是挑中了 plan_cache_mode,而是拒绝了五项不能归因的变化。
hypothesis
预测若成立:
可证伪点:
为什么不用 pg_stat_statements total planning time 直接判
它可以作为证据,但:
track_planningpolicy/overhead;- shared cumulative state;
- prepared custom/generic lifecycle;
- queryid aggregation;
- workload 重叠;
- planning 减少不一定转成 end-to-end SLO。
所以本实验直接测 client outcome,并用 prepared counters/plan probe 解释机制。
作用域
不使用:
因为本轮只回答 hypothesis,不创建持久 desired state。
safety target
exercise 会真实制造 CPU/I/O/WAL/replication load,不能在 production 运行。
一次性连接边界
临时 database 创建为:
为什么多这一步?
Pigsty exporter 可自动发现 database 并建立观测连接。若先开放 PUBLIC,再跑数分钟,
exporter 的 idle connection 会让普通 DROP DATABASE 安全拒绝。runner 不应:
先收紧 connect privilege,从源头避免 observer race。
database 与 role 都写:
清理精确比对 marker;只允许等待 autovacuum 自然退出,不强杀。
数据与 workload
M scale:
mix:
| operation | weight | distribution |
|---|---|---|
| product read | 50% | product Zipf 1.15 |
| order read | 30% | customer Zipf 1.08 |
| place order | 20% | customer uniform、product Zipf 1.10 |
每个 arm:
这组短 run 仍是教学证据,不是 production capacity characterization。
配对和 counterbalance
每次 measured run 前:
- truncate live orders;
- reset inventory data;
- 不 reset shared statistics;
- 不 drop cache;
- 不 force checkpoint;
- 不 restart;
- 不暂停 autovacuum/replication/archive。
每对相同 seed 降低 workload sampling noise;顺序交错降低时间趋势偏差。
predeclared rule
candidate 进入更大 canary 的必要条件:
最后一条同时表达:
通过也只代表 “candidate-worthy-for-larger-canary”,不是 production apply。
27.7.2 比较收益、副作用和故障恢复
运行
静态检查:
完整实验:
PG36_EVIDENCE_DIR 必须是不存在或为空的私密绝对目录。all:
正式 run identity
所有 run:
arm 聚合
| arm | runs | tx | median TPS | pooled p50 | pooled p95 | pooled p99 | max |
|---|---|---|---|---|---|---|---|
auto |
5 | 178,823 | 2,981.72 | 1.279 | 9.217 | 13.127 | 73.540 |
force_generic_plan |
5 | 178,862 | 3,051.72 | 1.285 | 9.135 | 12.933 | 77.943 |
latency 单位 ms。p50/p95/p99 从每个 arm 的 raw transaction sample 合并后重算。
为什么不能用 median TPS 除法
直接:
看起来正好超过 2%。但运行是配对设计,正确 effect:
$$ r_i
\frac{TPS_{\text{force},i}} {TPS_{\text{auto},i}} $$
五个 ratio 的:
区间既包含负收益,又没有达到预设下界。candidate pooled p95 ratio:
tail gate 通过,但 material-gain gate 失败。
plan probe
对 product/order 两个 read prepared statement,各执行 10 次:
| mode | query | custom | generic |
|---|---|---|---|
| auto | product | 5 | 5 |
| auto | order | 5 | 5 |
| force generic | product | 0 | 10 |
| force generic | order | 0 | 10 |
auto 不是“每次都 custom”;它在前五次收集 custom cost 后,已经为这两条稳定 point/
range lookup 选 generic。
对每个 query 测:
plan shape 只保留:
再计算 SHA-256。auto/candidate 四个 probe shape 相同。
这支持:
不支持:
机制解释
在 12 秒、高 transaction count 的 run 中,早期差异被摊薄,因此很难产生稳定 2% 收益。这与实验结果一致。
副作用
强制 generic 的风险不是本轮四个 shape,而是 parameter-sensitive workload:
global/role 强制会禁止 planner 对具体 parameter 自适应。收益证据弱,潜在 plan blast radius 大,所以即便 p95 没退化也不应接受。
global settings 无变化
实验前后逐项比较:
结果完全一致。参考 baseline:
| parameter | value | context | source |
|---|---|---|---|
plan_cache_mode |
auto | user | default |
work_mem |
65536 kB | user | config file |
shared_buffers |
62592 × 8kB | postmaster | config file |
max_connections |
500 | postmaster | command line |
synchronous_commit |
on | user | default |
wal_compression |
lz4 | superuser | config file |
raw sourcefile path 只在私密 evidence,不进入公共 JSON。
故障与恢复边界
因为 candidate 只在 session:
这使 hypothesis test 的 rollback 简单,但不代表 persistent rollout 也简单。
如果实验通过并要配置 role:
还需:
- 新 session/pool recycle;
- workload-wide parameter skew probes;
- failover 后 catalog replication;
- canary identity;
- revert;
- observation window。
本轮没有进入这一步。
cleanup
正式 run 证明:
清理证据是实验的一部分。不能只因 fixture “名字看起来像测试库”就 drop。
27.7.3 输出参数 ADR、回退条件与拒绝修改项
ADR
拒绝项
work_mem increase
如果未来 report spill,按 report role/query 单独实验。
shared_buffers increase
下一证据是 longer/XL working set 与 pg_stat_io/device latency,不是立即 restart。
max_connections increase
下一动作是 pool/admission 与 c2–c32 sweep。
synchronous_commit=off
它需要产品/RPO 决策,不是本章 performance shortcut。
change wal_compression
unknown backlog
- planning 与 execution CPU 的直接分解;
- c2/c4/c12/c16… 的 precise knee;
- open-loop offered-load SLO;
- realistic application/PgBouncer path;
- parameter-sensitive tenant skew;
- long-duration background overlap;
- production hardware;
- N+1/failover envelope。
这些 unknown 不阻止本次“拒绝”,因为 candidate 必须证明自己;没有足够收益证据时 baseline 胜出。
28 个反例
validator 必须拒绝:
positive evidence 通过而 negative evidence 也通过的 validator 没有保护力。
evidence bundle
正式 review:
公共 allowlist:
独立练习
练习一:把结论做错。
只用两个 arm 的 median TPS,写出“提升 2.35%,接受”。再用 paired ratio 重算,解释 为什么结论翻转。
练习二:设计 skew probe。
构造一个 tenant-size 极不均匀的 prepared query,比较:
先写 correctness/plan/tail/memory gate,不要先预设 generic 更好。
练习三:为 work_mem 写拒绝 ADR。
只允许使用 ch26 evidence。说明为什么 temp_bytes=0 足以拒绝 increase,却不足以批准
global decrease。
练习四:把 session candidate 变成 role canary。
只写 runbook,不执行。包括:
完成检查表
- objective 与 non-regression 在实验前声明。
- observed service center 与 parameter mechanism 有证据连接。
- 一次只测试一个机制。
- 使用最小可逆 scope。
- A/B 配对、seed 和顺序可复现。
- p95 从 raw sample 重算。
- effect 报区间,不只报两个 median。
- correctness、plan、failure 与 resource 同时验证。
- desired/configured/effective 没有漂移。
- cleanup 不使用 force 或误杀。
- 未测试参数明确拒绝。
- candidate 不通过时没有“为了有成果”落盘。
- production gate 在 production evidence 不足时保持 pending。
当最终结果是“不改”,而团队能清楚说明为什么,这套调优流程才真正成熟。
上一节:模板参数与集群变更 · 返回本章目录 · 下一章:除旧布新:VACUUM、冻结与膨胀治理 · 查看全书目录 · 查看索引中心