用 pg_shmemviz 看懂 PostgreSQL 共享内存:从分配布局到原始字节

2026-08-20 41 预计阅读时间: 1 分钟
来源: postgr.es AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

PostgreSQL 的共享内存决定了缓冲区、进程状态、WAL、锁和动态共享内存等多个核心子系统如何协作。过去,我们通常通过 pg_shmem_allocationspg_buffercachepg_shmem_allocations_numa 查看其中某个方面;但当问题涉及物理位置、C 结构体字段、指针引用和字节级变化时,这些视图就不够用了。

pg_shmemviz 是一个面向开发和调试的可视化工具。它把 PostgreSQL 的主共享内存段和动态共享内存段捕获为离线快照,再通过本地浏览器展示共享内存地图、分配表、结构体字段和原始字节。不同视图相互联动:选择一个分配区域、结构体字段或字节,就能跳转到其他视图对应的位置。

从共享内存地图定位真实分配

共享内存概览会展示命名分配、分配器填充区域以及未使用范围。主共享内存、DSM 控制段、DSM 段和 DSA 段可以分别查看。通过过滤分配表、选择某个分配或缩放到特定物理范围,可以从整体布局逐步缩小到具体区域。

这种视角对排查以下问题尤其有帮助:

  • 某个 PostgreSQL 子系统实际占用了多少共享内存。
  • 结构体之间是否存在编译器填充或数组步长填充。
  • 某个指针指向的区域位于哪个共享内存段。
  • 两次运行之间,某个字段对应的原始字节究竟发生了什么变化。

pg_shmemviz 会使用与目标服务器完全匹配的 postgres 可执行文件中的 DWARF 调试信息。结构检查器不仅能显示嵌套 C 结构体,还能展示字段偏移、字段值、编译器填充、数组步长以及有边界信息的指针目标。选中指针目标后,工具会同步标记源指针,并打开目标区域的字节视图。

结构字段、指针和原始字节

Physical Bytes 视图把一段有边界的内存窗口按结构体字段和填充区域分类。选择一个字节,可以定位到已知的最深层字段;选择字段,则会在原始字节中框出其对应范围。鼠标悬停在字节上时,还可以查看地址、偏移、数值、所属字段、NUMA 节点以及可用的快照比较信息。

工具还针对 PostgreSQL 内部常见结构提供了专门发现逻辑,包括:

  • 统计信息结构。
  • WAL 相关结构。
  • 进程和 PGPROC 结构。
  • SLRU 结构。
  • dynahash 结构。
  • DSM 注册表结构。

如果安装了 pg_buffercache 扩展,快照还可以包含缓冲区缓存摘要。可选的缓冲区详情包括缓冲区编号、数据库、表空间、关系、fork、数据块、脏页状态、使用计数以及正在 pin 它的后台进程。工具还可以解析对象名称,并在缓冲区描述符和对应页面之间直接导航。

DSM、DSA 和 NUMA 信息

动态共享内存会与主共享内存分开展示。DSM 注册表中的名称可以帮助识别普通 DSM 段、命名 DSA 区域,以及为已知 dshash 表提供后端存储的 DSA 区域。

在使用 --with-libnuma 构建的 PostgreSQL 上,地图还可以显示页面的 NUMA 放置情况。NUMA 信息会沿着分配区域、结构体实例、字段、指针引用区域和原始字节继续传递。一个跨越页面边界的结构体可能同时显示多个 NUMA 节点,而数组元素可以拥有比父结构更精确的节点信息。

默认捕获范围是 ProcGlobal->allProcs。也可以显式请求共享缓冲区,或捕获主共享内存中全部命名分配。

安装、捕获和查看快照

当前版本是 v0.1.0-beta.1。使用前需要准备一个仅用于开发或调试的 PostgreSQL 实例,并确保以下条件满足:

  • PostgreSQL 服务器包含调试信息。
  • 捕获时使用的 pg_configpostgres 可执行文件和开发文件彼此匹配。
  • macOS 使用 LLDB,其他平台通常使用 GDB。
  • 调试器只读取可执行文件中的 DWARF,不会附加到正在运行的服务器进程。

下面是一组可直接改造的命令。请把路径替换为本机 PostgreSQL 安装目录和 pg_shmemviz 源码目录:

set -eu

PG_CONFIG=/opt/postgresql/bin/pg_config
PG_SHMEMVIZ=$HOME/src/pg_shmemviz
SNAPSHOT=$HOME/tmp/pg-shmem-snapshot

cd "$PG_SHMEMVIZ"
make PG_CONFIG="$PG_CONFIG"
make PG_CONFIG="$PG_CONFIG" install

"$PG_CONFIG" --bindir/psql -d postgres \
  -c 'CREATE EXTENSION IF NOT EXISTS pg_shmemviz'

"$PG_SHMEMVIZ/bin/pg_shmemviz" capture \
  --pg-config "$PG_CONFIG" \
  --dbname postgres \
  "$SNAPSHOT"

"$PG_SHMEMVIZ/bin/pg_shmemviz" serve "$SNAPSHOT"

如果 pg_config 不在常见安装路径,可以先执行以下命令确认服务器和工具使用的是同一套 PostgreSQL 文件:

PG_CONFIG=/opt/postgresql/bin/pg_config
"$PG_CONFIG" --bindir
"$PG_CONFIG" --includedir-server
"$PG_CONFIG" --version

比较两个兼容快照时,可以把它们同时传给 viewer:

pg_shmemviz serve \
  /path/to/before-snapshot \
  /path/to/after-snapshot

比较视图会保留 Before 和 After 两侧的匹配选择。例如,某个事务统计字段从 1736 变为 1952 时,界面会在 Physical Bytes 中框出对应的八字节字段,并用紫色标记实际发生变化的字节。

对于远程或无桌面的开发服务器,可以让服务只监听本机,再通过 SSH 隧道访问:

pg_shmemviz serve \
  --no-open \
  --port 8765 \
  /path/to/new-snapshot

ssh -N -L 8765:127.0.0.1:8765 user@server

随后在开发机浏览器中打开 http://127.0.0.1:8765/。viewer 默认绑定 127.0.0.1:8765,且没有认证、授权或 TLS,因此不应直接暴露在网络接口上。

使用边界与实际建议

捕获过程是对实时共享内存进行顺序、无锁复制。PostgreSQL 在复制期间仍可能修改这些内容,因此相关字段未必来自同一时刻;多字节的非原子字段也可能出现撕裂。pg_buffercache 元数据与复制出的共享内存字节同样可能对应不同时间点。

完整的主共享内存段会被复制,其中包括共享缓冲区和预留但尚未使用的空间,所以快照可能很大,也可能包含敏感数据。启用 NUMA 捕获还可能触发未访问页面的缺页处理,并建立首次触碰时的页面放置位置。

因此,建议把 pg_shmemviz 当作一次性的开发诊断工具使用:

  • 只在可丢弃或隔离的 PostgreSQL 实例上运行。
  • 避免在生产数据库上捕获共享内存。
  • 将快照文件视为可能包含数据库敏感信息的调试产物。
  • 确保捕获服务器与解析结构布局的 postgres 可执行文件完全匹配。
  • 远程访问时保持服务监听回环地址,并使用 SSH 隧道。
  • 对自动结构发现保持版本意识;当前验证重点是 PostgreSQL 20devel,其他版本应结合兼容性说明验证。

对于需要回答“某个共享内存对象在哪里”“这个字段占用了哪些字节”或“两次运行具体改变了哪些内存内容”的调试任务,pg_shmemviz 提供了比 SQL 视图更接近底层实现的观察方式。它仍处于 beta 阶段,但已经把共享内存布局、C 结构体、指针关系、NUMA 放置和快照差异放进了同一个可导航界面。


相关推荐