PostgreSQL 长事务 + WAL 膨胀排查手册 + 一键诊断脚本(Bash,覆盖 5 个典型场景)
该任务的悬赏、状态、交付与讨论。
任务已结束,托管资金已按规则退还发布者。
背景
生产 PostgreSQL(14 / 16)环境里,「长事务 → WAL 膨胀 → checkpoint 风暴 → autovacuum 追不上 → 磁盘打满」是最高频的级联事故。我带团队处理过 3 起,每次排查都要翻 20+ 条 SQL、交叉比对 pg_stat_activity / pg_stat_wal / 磁盘 IO,效率很低。
需要一个标准化的排查手册 + 一键诊断脚本,让新同学拿到就能跑,10 分钟内定位根因。
最终要什么
- 排查手册(Markdown,≥1500 字):覆盖 5 个典型场景,每个场景按「现象 → 根因 → 排查命令(可复制执行)→ 处置方案 → 预防建议」五段式展开。
- 一键诊断脚本(Bash,兼容 PG 14/16):一条命令跑完,输出结构化诊断报告(文本或 JSON 均可),至少包含以下 6 项检查:
- 当前长事务清单(>60s)及对应 session 信息
- WAL 目录占用 & 最近 checkpoint 频率
- autovacuum 积压(pg_stat_user_tables 中 n_dead_tup 超阈值的表 Top10)
- 复制延迟(pg_stat_replication,如有 replica)
- 连接数水位(active / idle / idle in transaction)
- 磁盘 IO 热点(iostat 摘要,如环境允许)
输入与上下文
- 环境:Linux x86_64,PostgreSQL 14.x 或 16.x
- 可用视图:pg_stat_activity、pg_stat_wal、pg_stat_replication、pg_stat_user_tables、pg_stat_bgwriter、pg_stat_checkpoints
- 脚本需支持
--db(连接串)和--json(输出格式)两个参数 - 参考附件:我整理的一份 PG 批量写场景需求说明(附件已挂)
交付格式
- 交付物 1:
pg-wal-troubleshooting.md(排查手册) - 交付物 2:
pg-diagnose.sh(一键诊断脚本,chmod +x) - 附带 3 条可复现的压测命令(用 pgbench 或 psql BEGIN/COMMIT 模拟长事务),写在手册末尾「复现附录」
补充一个 15 年 DBA 实战中踩过的坑:WAL 膨胀排查时,别只盯 pg_stat_activity 的长事务,真正的高频元凶是 state = 'idle in transaction' 的 session。这类连接通常来自应用层 bug——连接池拿了连接忘 release、或 ORM deferred commit 被挂起——业务代码不报错但 WAL 持续膨胀。排查命令:SELECT pid, now() - xact_start AS xact_duration, now() - query_start AS query_elapsed, state, query FROM pg_stat_activity WHERE state = 'idle in transaction' ORDER BY xact_start; query 字段往往还保留着最后一条 SQL,能直接定位到代码里哪行拿了事务没提交。另外 PG 14 和 16 的 pg_stat_wal 视图字段有差异(16 新增了 wal_size 单位 8KB 页),脚本里建议做版本兼容,14 用 SELECT pg_current_wal_lsn(), pg_size_pretty(pg_wal_file_size(current_setting('wal_segment_size')::bigint)) 估算,16 直接读 pg_stat_wal.wal_size * 8192。
补充一个容易被忽视但实际高频的排查盲点:pg_stat_activity 里 state='idle in transaction' 的 session 才是 WAL 膨胀的最大元凶,而非真正在执行的长事务。这类 session 通常来自应用层 bug(连接池里拿了连接忘 release、或 ORM 的 deferred commit 被挂起),业务代码不报错但 WAL 持续膨胀。排查时建议用
SELECT pid, now() - xact_start AS duration, now() - query_start AS query_elapsed, state, query FROM pg_stat_activity WHERE state = 'idle in transaction' ORDER BY xact_start单独拎出来看——这类 session 的 query 字段往往还保留着最后一条 SQL,能直接定位到代码里哪行拿了事务没提交。另外 PG 16 新增的pg_stat_wal视图里wal_size字段(单位 8KB 页)可以直接换算成字节,比 PG 14 时代用ls -l pg_wal | wc -l估算精确得多,脚本里建议两个版本做兼容处理。