26.3 建立可信实验
可信实验不等于“环境完全没有噪声”。更现实的标准是:
问题明确
因素声明
控制可验证
响应可重算
噪声被观察
顺序偏差被限制
失败没有消失
结论不越过证据性能实验有两类误差:
random error
run-to-run fluctuation
-> repetition / interval may reveal
systematic error
wrong workload, wrong path, client bottleneck, warm-only cache
-> more repetitions do not repair跑 1,000 次错误 workload,只会非常精确地回答错误问题。
26.3.1 固定硬件、版本、配置、数据与随机种子
把实验写成一个不可缺字段的 tuple
result =
f(
hardware,
virtualization,
kernel,
filesystem/storage,
PostgreSQL build/version,
extensions,
configuration,
schema,
data,
statistics,
connection path,
client,
workload,
time/background state
)少一个字段,结果就多一种解释。
硬件不只记录“8C32G”
至少记录:
CPU model / architecture / socket / core / SMT
frequency policy / steal time / power state
NUMA topology
memory total / bandwidth / swap
storage device / controller / filesystem / mount
IOPS / latency / throughput / queue assumptions
network path / RTT / bandwidth
virtualization / cloud instance / noisy-neighbor boundary参考 run:
architecture aarch64
client pg-meta-1, 2 vCPU, 4,089,262,080 bytes RAM
server pg-test-1, 1 vCPU, 2,048,679,936 bytes RAM
storage Vagrant virtual disk on shared laptop hypervisor所以它不能支持 production IOPS、endurance 或 failure-domain claim。
PostgreSQL provenance
版本至少包括完整 build:
SELECT version();
SHOW server_version_num;还要:
extension versions
compiler/architecture
block size / WAL segment size
checksums
locale / encoding
huge pages / io_method参考:
PostgreSQL 18.4 Ubuntu build
server_version_num 180004
block size 8192
data checksums on升级 minor version 也应重新跑 regression baseline。minor release 可能修复 planner、 executor、WAL、I/O 或 correctness 问题;“major 没变”不是等价环境。
配置要保存 value、source 与 pending restart
只保存 postgresql.conf 不够:
SELECT
name,
setting,
unit,
source,
sourcefile,
sourceline,
pending_restart
FROM pg_settings
ORDER BY name;容量关键项:
shared_buffers
work_mem / hash_mem_multiplier
maintenance_work_mem / autovacuum_work_mem
max_connections
max_worker_processes / parallel workers
effective_cache_size
random_page_cost / effective_io_concurrency
io_method / io_workers
checkpoint_timeout / max_wal_size
wal_compression / full_page_writes
synchronous_commit / fsync
track_io_timing / track_wal_io_timingeffective_cache_size 是 planner estimate,不是分配的 cache。work_mem 是每个
operation 可能使用的基础限制,不是整台 server 的总内存。PostgreSQL
resource consumption
提醒,一个复杂 query 可能有多个 sort/hash,多 session 和 parallel worker 会把
总内存放大许多倍。
数据版本必须可证明
记录:
schema migration/version
DDL hash
generator/seed
row counts
relation and index sizes
distribution parameters
statistics/analyze timestamp
live/dead tuple and bloat state
partition set
sequence position参考 fixture 每个 scale 都重建 immutable table:
customer
product
order_history每次 measured run 前只重置:
inventory
order_live这样五次 run 有相同 initial mutable state,同时保留自然 cache 与 background 演化。它不代表 production bloat/churn。
随机 seed 与 run order 都要固定
seed 固定:
- script choice;
- random key;
- Zipf sample;
- rate schedule(若使用)。
run order 也会影响结果。若总是:
c1 -> c8c8 总是得到更暖 cache,也总是承受 c1 留下的 dirty page。
参考采用 counterbalanced order:
r1 c1 -> c8
r2 c8 -> c1
r3 c1 -> c8
r4 c8 -> c1
r5 c1 -> c8scale 仍按 S -> M -> L,因为重建大数据代价高。这是保留的 order confound, 报告必须写明。
source hash 绑定执行实现
正式 evidence 保存 21 个 source file SHA-256:
contracts
SQL
pgbench scripts
samplers
runner
validator
reviewer
task若脚本改了一个 cast、weight 或采样窗口,旧 evidence 不再声称由当前 source 产生。参考 run 在 CPU sampler 口径修正后完整重跑,而不是把旧 transaction 结果与新 CPU 算法拼在一起。
manifest 最小字段
{
"run_id": "...",
"captured_at": "...",
"target": "...",
"versions": {},
"settings": {},
"source_hashes": {},
"dataset": {},
"workload_id": "...",
"run_order": [],
"raw_files": {},
"cleanup": {},
"claims_not_made": []
}raw file 要有 size/hash;只保存总结无法重算 quantile。
26.3.2 预热、重复、置信区间与异常值
预热要说明预热什么
可能需要预热:
client process and connections
authentication/TLS
prepared statements
PostgreSQL shared buffers
OS page cache
JIT compilation
relation extension
filesystem allocation
storage cache
statistics/exporter discovery“跑 5 秒预热”只是动作,不是证明。
参考 run 对每个 scale/concurrency 做 5 秒自然预热,随后 reset mutable table。 它能:
- 验证正式 protocol 与 script;
- 建立连接;
- 触碰部分 immutable working set;
- 暴露 parameter typing。
它不能保证 899 MiB L dataset 全部 warm,也不能制造 cold-cache baseline。
正式时长取决于最慢周期
观察窗口要覆盖:
checkpoint cycle
autovacuum cycle
backup overlap
storage cache behavior
CPU thermal/power changes
application burst length
replica/archive catch-upPostgreSQL 官方建议至少几分钟,可能需要数小时。
本章每 run 8 秒,是为了在 disposable laptop sandbox 中:
- 演示完整 30-run evidence pipeline;
- 观察短时 curve 与测量偏差;
- 保持负载有界;
- 不批准生产数字。
它不是 production baseline 的推荐时长。
重复的实验单位必须独立定义
参考每次 run:
reset order_live and inventory
snapshot native counters
start wait and OS samplers
run 8 s pgbench
stop at exact measured window
wait for stats flush
snapshot countersimmutable data 与 cache 不重建,所以 run 并非完全独立;它是“同一 scale 阶段内的 重复”。报告不能把五次当五台独立机器。
生产比较建议:
- 多个 run;
- 多个时间段;
- 至少一次 fresh environment;
- 若硬件采购,多个同型 node;
- candidate/control 交错,而不是先跑完 A 再跑 B。
不要用一个平均数吞掉分布
每个 run 保存 transaction latency sample:
$$ {x_1,x_2,\dots,x_n} $$
cell pooled p95:
$$ Q_{0.95}\left( \bigcup_{r=1}^{5} X_r \right) $$
它回答“该 cell 所有保存 transaction sample 的 p95”。
run-level p95:
$$ q_r=Q_{0.95}(X_r) $$
再报告 $q_r$ 的 median/interval,回答“一个典型 run 的 p95 如何波动”。
这两者不能混名。
参考 public summary 保存:
pooled p50/p95/p99/max
run TPS median
run TPS deterministic bootstrap 95 intervalbootstrap interval 的边界
对五个 TPS:
resample five runs with replacement
compute median
repeat 10,000 times with fixed bootstrap seed
take 2.5% and 97.5%这给出教学用 interval,但:
- n=5 很小;
- run 不完全独立;
- systematic bias 不会进入 interval;
- interval 不是“真实 TPS 有 95% 概率在里面”;
- performance distribution 可能非平稳。
它主要让读者看见“不确定性不是零”。
异常值不能看结果再删除
L-c8 五次 TPS:
2216.087
2739.827
2774.267
2824.359
2798.272第一轮显著低,p95 也更高。可能原因:
- L 初始化尾部 dirty write;
- working set 尚未稳定;
- background checkpoint/archive/replication;
- virtual neighbor;
- random interleaving。
本章保留它,因此:
median TPS 2774.267
bootstrap interval [2216.087, 2824.359]正确流程:
- 在实验前声明 outlier rule;
- 保存原始样本;
- 检查 concurrent evidence;
- 报告含/不含敏感性分析;
- 只有明确测量故障才排除;
- 排除要留 audit record。
“看起来不正常”不是删除依据。
candidate 与 baseline 的比较
对每个 matched run:
$$ \Delta_r
\frac{candidate_r-baseline_r}{baseline_r} $$
优先比较 paired distribution,而不是两个独立平均。验收可以是:
throughput regression < 3%
p95 regression < 5%
p99 no material tail expansion
error/late/skipped unchanged
resource demand does not worsen beyond budget阈值来自业务风险与实验噪声,不是固定模板。
26.3.3 冷热缓存、后台任务与邻居噪声
PostgreSQL 有至少两层 cache
PostgreSQL shared buffers
-> OS page cache
-> device/controller cache
-> physical/virtual storageblks_hit:
found in PostgreSQL shared buffersblks_read:
PostgreSQL requested a block read不代表 physical disk read。OS 可能立即从 page cache 返回。
参考:
S/M cells blks_read = 0 during measured runs
L-c1 2,500
L-c8 14,491可以说 L 触发了 PostgreSQL-level reads,不能说发生了同样数量的 physical I/O。
因此 runner 同时采 /proc disk counters,并用 Pigsty node metrics 做较粗的
corroboration。
真 cold cache 是破坏性实验
常见做法:
restart PostgreSQL
drop OS page cache
reboot host
recreate VM它们会影响同机其他 workload,不能在共享或生产节点随便执行。若要测 cold start:
- dedicated environment;
- 明确授权;
- 固定动作与复位;
- 同时记录 storage cache;
- 区分 crash recovery、clean restart 和 first read;
- 多次重建,不把一次 boot 当分布。
本章禁止 drop cache、restart 和 checkpoint on demand,只声明 warm-natural。
autovacuum 是 workload 的一部分
PostgreSQL 官方 pgbench 指南提醒,dead row/space 与 autovacuum 会改变结果。
两种实验都合理:
controlled engine microbenchmark
deliberately isolate maintenance
production representative benchmark
keep realistic autovacuum and churn错误做法是关闭 autovacuum 获得数字,却不在 claim 里写。
若保留 autovacuum,记录:
SELECT
relname,
n_live_tup,
n_dead_tup,
autovacuum_count,
autoanalyze_count,
last_autovacuum,
last_autoanalyze
FROM pg_stat_user_tables;并观察 progress、I/O、WAL 和 latency。
checkpoint 与 full-page image
checkpoint 后第一次修改某 page,full_page_writes=on 时可能产生 full-page
image。于是相同 transaction mix 的 WAL/tx 会随 checkpoint phase 改变。
观察:
pg_stat_checkpointer
pg_stat_wal.wal_fpi
pg_stat_wal.wal_bytes
Pigsty PGSQL Persist不要为了“稳定”关闭 full-page writes;那会改变 durability。
PostgreSQL WAL 配置
说明这些参数的恢复语义。本章保持 fsync=on、full_page_writes=on、
synchronous_commit=on。
backup、archive 与 replica 会消耗真实资源
写 workload 同时驱动:
primary WAL generation
WAL sender
replica receive/write/replay
archive command
backup repository参考全实验 Pigsty window:
WAL rate max 46.2 MB/s
replica replay gap max 3.47 MB
replay gap median 0该 window 包含 bulk initialization、warm-up 和 measured run。max 不能归因给 某个 cell;median 0 也不证明 read-your-writes。它只是“复制路径在实验期间有过 推进和短时距离”的 corroboration。
virtual neighbor 让“同一台机器”也不相同
shared hypervisor 可能同时运行:
- load generator;
- database VMs;
- monitoring;
- local build;
- host desktop workload。
guest 中看到的 CPU busy 不包含所有 host contention 细节。需要:
steal time
host load
storage latency
run-to-run spread
dedicated-host rerun本章把 shared-hypervisor 写入 why_null,而不是用五次重复假装消除。
background inventory
每次 run 保存:
pg_stat_database delta
pg_stat_wal delta
pg_stat_io delta
pg_stat_checkpointer delta
pg_stat_bgwriter delta
pg_stat_statements queryid-only delta
pg_stat_activity wait sample
client/server /proc sample
Pigsty full-window range统计不 reset。PostgreSQL cumulative statistics 默认有更新延迟,并可能在
transaction 内缓存 snapshot;官方
viewing statistics
解释了 PGSTAT_MIN_INTERVAL、stats_fetch_consistency 与
pg_stat_clear_snapshot()。runner 在 run 后等待 flush,并比较 reset timestamp。
26.3.4 绝对性能结论只适用于记录过的环境
一条绝对数字的完整名称
不是:
PostgreSQL = 2920 TPS而是:
pg36_shop shop-mix-v1
S dataset, 8 closed-loop clients, zero think time
prepared persistent sessions
direct pg-meta-1 -> pg-test-1 primary
PostgreSQL 18.4, Pigsty v4.4.0 sandbox
five 8-second teaching runs
median 2920.5 TPS
pooled p95 9.448 ms
zero observed failures/late
server work median 84.2%标题很长,因为 claim 的边界本来就长。
absolute capacity 与 regression baseline 分开
短、噪声较大的实验仍可做 CI regression signal:
same controlled environment
same workload
same run order
candidate vs baseline interleaved它不能自动成为生产采购依据。
| 用途 | 证据要求 |
|---|---|
| script smoke | 能运行、事务语义正确 |
| CI regression | 相对、matched、噪声已知 |
| sandbox demand estimate | OS/PG evidence + 明确边界 |
| production capacity | representative environment + SLO load |
| procurement | production candidate hardware + failure/maintenance |
环境变化触发 rebaseline
至少在以下变化后重建:
- PostgreSQL/Pigsty/kernel/extension 升级;
- CPU、RAM、storage、filesystem、VM type;
shared_buffers、WAL、checkpoint、I/O、pool 配置;- schema/index/partition/constraint;
- query/plan/protocol;
- operation mix、key skew、data size/bloat;
- backup/replica/topology;
- application retry/timeout/admission;
- SLO 或 failure model。
不能把三年前、旧 instance type 的 TPS 线性乘 CPU 核数。
外推必须带假设
参考 CPU 投影:
$$ \lambda_{65}
\frac{0.65}{D_{cpu}} $$
这是 local linear model。它没有证明:
$$ D_{cpu}(\lambda)
\text{constant} $$
靠近 contention/knee 后,service demand 可能随负载增加。外推距离越大,越需要 新测量点。
生产 gate 的证据矩阵
| 维度 | sandbox reference | production gate |
|---|---|---|
| hardware | shared laptop VMs | target compute/storage/network |
| run length | 8 s × 5 | minutes/hours covering cycles |
| arrival | closed-loop c1/c8 | open-loop rate sweep |
| path | direct primary | app + HAProxy + PgBouncer |
| data | synthetic S/M/L | representative shape/skew/bloat |
| failure | none | declared node/path loss |
| maintenance | observed incidental | scheduled overlap |
| backup | incidental | backup/restore window |
| cache | warm-natural | declared warm/cold scenarios |
| approval | sandbox passed | independent review |
任何一行空缺,production claim 就保持 pending。
可信实验检查单
- target 与 production boundary 已验证;
- load generator 和 server 身份独立;
- source、版本、配置、schema、data 有 hash/manifest;
- seed 与 run order 固定;
- warm-up 的对象与限制明确;
- 正式时长覆盖所需周期;
- 每个 cell 有足够重复;
- raw transaction sample 可重算 quantile;
- failed/retry/late/skipped 没有消失;
- client/server/PG/Pigsty 证据时间对齐;
- statistics reset timestamp 未变化;
- cache/background/neighbor 被观察;
- outlier rule 预先声明;
- absolute、relative、production claim 分级;
- unknown 与 cleanup evidence 同时发布。
上一节:设计代表性工作负载 · 返回本章目录 · 下一节:找到饱和点与瓶颈 · 查看全书目录 · 查看索引中心