12.5 服务级可观测性
一次请求跨过 HTTP、应用 pool、PgBouncer、PostgreSQL transaction 与 outbox。每层都有自己的 identity:
把它们全塞进一个字符串会失去可聚合性;一个都不关联又无法从用户症状走到数据库证据。
12.5.1 请求、事务、查询指纹与 application_name
Identity 分层
本章的关联关系:
trace_id 告诉你一次调用从哪里来;request_key 告诉数据库它是否与先前命令是同一个业务意图。两者不能互换:
- retry 可以有新 trace,但保留同 idempotency key;
- 同一 trace 可能跨多个内部调用;
- trace 通常有保留期,idempotency ledger 是业务状态;
- trace 不应承担 uniqueness/authorization。
application_name 保持低基数
本章所有业务 backend:
不要每请求改成:
否则:
- metrics cardinality 爆炸;
pg_stat_activity分组失去意义;- PgBouncer tracking/reset 行为更复杂;
- 日志与 dashboard 成本上升;
- 可能泄露 tenant/user。
合理粒度:
必要时附固定 version/channel,但先评估 cardinality。per-request identity 放结构化 log/trace,不放 session label。
Query identity 不等于 raw SQL + values
观测 query 应优先关联:
- PostgreSQL
query_id/pg_stat_statements.queryid; - normalized query text;
- driver operation name;
- route/query bundle mapping;
- application version。
不要把 password、token、PII、完整 JSON 或 payment data 作为 metric label。参数对诊断重要时:
- 保存经过批准的 bounded class,例如
sku_class=known/missing; - 对值 hash/pseudonymize;
- 只在短期受控 evidence 中保留;
- 遵循日志保留与访问政策。
第 8 章已说明 query text 与 parameter identity 缺一不可;本章增加了 API route 与 trace,但没有取消隐私边界。
Transaction attempt 与 logical request
trace-retry-001 的服务请求只返回一次,数据库执行了两个 transaction attempt:
metrics 同时需要:
如果只看 HTTP 201,会漏掉 serialization pressure;如果把每次 attempt 都算一个请求,会夸大业务流量。
结构化日志
正常请求:
数据库 timeout:
日志不包含:
不是所有错误都要打 stack;预期 domain conflict 应按可聚合 code 记录,真正未知 defect 才需要更深内部 cause。
12.5.2 延迟、错误、连接等待和数据库等待
一个慢请求至少有四段
[ T_{request} = T_{app}
- T_{pool}
- T_{db}
- T_{response} ]
T_db 还可拆:
仅看总延迟无法决定加 index、加 connection 还是减并发。
应用 pool wait
pgxpool 暴露:
本章转换成:
CanceledAcquireCount 上升表示请求在拿到 DB connection 前就耗尽 context。此时 PostgreSQL pg_stat_activity 看不到对应 query;从数据库侧“没有慢 SQL”并不能证明数据库路径无关,连接预算或 pool queue 可能已经挡在前面。
PgBouncer wait
通过 Pigsty primary service 时还有 PgBouncer 队列。需要结合:
- PgBouncer client active/waiting;
- server active/idle;
- pool size/reserve;
- average/max wait;
- connection errors;
- user/database pool identity;
- HAProxy/service health。
app pool 不等待、PostgreSQL backend 也不多,仍可能是 PgBouncer client 在等 server slot。第 22 章会用 SHOW POOLS / SHOW STATS 和 Pigsty dashboard 深化。
PostgreSQL wait
取得 backend 后,观察:
典型解释:
| state/wait | 方向 |
|---|---|
| active + Lock | blocking graph |
| active + IO | plan/buffers/storage |
| active + Client* | application consume/send |
| idle in transaction | leaked transaction/locks/vacuum impact |
| no backend + app pool wait | app-side admission |
| PgBouncer waiting + few DB slots | pooler/server budget |
不要把 state=active 等同于“在用 CPU”;wait event 才说明当前等待类型。
Error 指标保留 SQLSTATE 与领域 code
本章同时记录:
一次运行:
SQLSTATE cardinality 是 bounded vocabulary;constraint name 通常也相对 bounded,但 table/tenant/raw error message 不适合无审查直接做 label。
Rate、error、duration 与 saturation 同看
最小服务面板:
平均值会隐藏 tail。pool wait 若只占 1% 请求,平均可能很小,p99 已经超时。
12.5.3 从一次请求追到数据库证据
一条可执行调查路径
用户报告下单超时,先固定:
然后沿层次走:
最后一步不是“SQL 后来成功了吗”,而是:
实测 trace 关系
本章 observer 独立于 app 查询:
这让 operator 能从 request 走到 committed fact,又保持 database session label 可聚合。
诊断一次 pool exhaustion
实验时间线:
证据组合:
这排除了“PostgreSQL query 本身超时”,并证明 admission queue 按预算失败。
诊断一次 statement timeout
若只看 504,会与 gateway timeout 混淆;若只看 57014,又会与 client cancel 混淆。timeline + source timeout + state comparison 才是完整证据。
Unknown commit 的调查
客户端 timeout 后不要先删 key 再重试。先用相同 idempotency key 查询/重放:
本章 schema constraint 允许 transaction 内暂时 response=NULL,但完整 transaction 失败会 rollback;正常 committed state 只能是 key+order/payment+response 完整。verify 把 incomplete committed row 当失败。
本节检查表
- trace、idempotency、application、query、event identity 分层;
-
application_name是低基数 workload class; - request retry 与 transaction attempt 分开计数;
- log 使用结构化 code,不泄露 secret/raw payload;
- request、pool、PgBouncer、DB 延迟可分别观察;
- pool canceled acquire 有独立 metric;
-
pg_stat_activity同时看 state 与 wait event; - SQLSTATE 与 domain code 都保留;
- tail latency 与 saturation 同窗;
- trace 能关联到 durable order/payment/outbox;
- unknown commit 通过 ledger 判断,不凭网络结果猜;
- 故障结论同时包含反证与 committed state。
参考资料
- PostgreSQL 18:Monitoring Database Activity
- PostgreSQL 18:pg_stat_activity
- PostgreSQL 18:Error Codes
- pgx v5.10.0:pgxpool Stat
- Pigsty:PostgreSQL Dashboards
- Pigsty:PgBouncer Administration
上一节:会话状态与连接池陷阱 · 返回本章目录 · 下一节:部署与接入 pg36_shop ·
查看全书目录 · 查看索引中心