选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0
历史版本PostgreSQL 9.0 已于 2015 年 10 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

27.4. 动态追踪 #

PostgreSQL提供了功能来支持数据库服务器的动态追踪。这样就允许在代码中的特 定点上调用外部工具来追踪执行过程。

一些探针或追踪点已经被插入在源代码中。这些探针的目的是被数据库开发者和管理员使用。默认情况下,探针不被编译到PostgreSQL中;用户需要显式地告诉配置脚本使得探针可用。

Currently, only the DTrace 工具支持,它可用于 OpenSolaris、Solaris 10 和 Mac OS X Leopard。预计 DTrace 将来也会在 FreeBSD 以及可能的其他操作系统上可用。Linux 上的SystemTap项目也提供了 DTrace 的等价物。通过修改src/include/utils/probes.h中宏的定义,理论上也可以支持其他动态追踪工具。

27.4.1. 为动态追踪编译 #

默认情况下,探针是不可用的,因此你将需要显式地告诉配置脚本让探针在PostgreSQL中可用。要包括 DTrace 支持,在配置时指定--enable-dtrace。更多信息请见第 15.5 节

27.4.2. 内置探针 #

源码中提供了许多标准探针,如表 27.3所示。 当然还可以添加更多探针来增强PostgreSQL的可观测性。

表 27.3. 内置 DTrace 探针

Name Parameters Description
transaction-start (LocalTransactionId) 在新事务开始时触发的探针。 arg0 is the transaction ID.
transaction-commit (LocalTransactionId) 事务成功完成时触发的探针。 arg0 is the transaction ID.
transaction-abort (LocalTransactionId) 事务未能成功完成时触发的探针。 arg0 is the transaction ID.
query-start (const char *) 开始处理查询时触发的探针。 arg0 is the query string.
query-done (const char *) 查询处理完成时触发的探针。 arg0 is the query string.
query-parse-start (const char *) 开始解析查询时触发的探针。 arg0 is the query string.
query-parse-done (const char *) 查询解析完成时触发的探针。 arg0 is the query string.
query-rewrite-start (const char *) 开始重写查询时触发的探针。 arg0 is the query string.
query-rewrite-done (const char *) 查询重写完成时触发的探针。 arg0 is the query string.
query-plan-start () 开始规划查询时触发的探针。
query-plan-done () 查询规划完成时触发的探针。
query-execute-start () 开始执行查询时触发的探针。
query-execute-done () 查询执行完成时触发的探针。
statement-status (const char *) 服务器进程更新其pg_stat_activity.current_query状态时触发的探针。arg0 是新的状态字符串。
checkpoint-start (int) 检查点启动时触发的探针。arg0 是用于区分不同检查点类型(如 shutdown、immediate 或 force)的位标志。
checkpoint-done (int, int, int, int, int) 检查点完成时触发的探针。(接下来列出的探针在检查点处理期间依次触发。)arg0 是写出的缓冲区数。arg1 是缓冲区总数。arg2、arg3 和 arg4 分别包含新增、删除和回收的 xlog 文件数。
clog-checkpoint-start (bool) 检查点的 CLOG 部分开始时触发的探针。正常检查点时 arg0 为真,关闭检查点时为假。
clog-checkpoint-done (bool) 检查点的 CLOG 部分完成时触发的探针。arg0 的含义与 clog-checkpoint-start 相同。
subtrans-checkpoint-start (bool) 检查点的 SUBTRANS 部分开始时触发的探针。正常检查点时 arg0 为真,关闭检查点时为假。
subtrans-checkpoint-done (bool) 检查点的 SUBTRANS 部分完成时触发的探针。arg0 的含义与 subtrans-checkpoint-start 相同。
multixact-checkpoint-start (bool) 检查点的 MultiXact 部分开始时触发的探针。正常检查点时 arg0 为真,关闭检查点时为假。
multixact-checkpoint-done (bool) 检查点的 MultiXact 部分完成时触发的探针。arg0 的含义与 multixact-checkpoint-start 相同。
buffer-checkpoint-start (int) 检查点的缓冲区写出部分开始时触发的探针。arg0 是用于区分不同检查点类型(如 shutdown、immediate 或 force)的位标志。
buffer-sync-start (int, int) 检查点期间开始写出脏缓冲区时(在识别出哪些缓冲区必须写出之后)触发的探针。arg0 是缓冲区总数。arg1 是当前脏且需要写出的缓冲区数。
buffer-sync-written (int) 检查点期间每写出一个缓冲区后触发的探针。arg0 是缓冲区的 ID 编号。
buffer-sync-done (int, int, int) 所有脏缓冲区都已写出时触发的探针。arg0 是缓冲区总数。arg1 是检查点进程实际写出的缓冲区数。arg2 是预期要写出的缓冲区数(buffer-sync-start 的 arg1);任何差值都反映了检查点期间其他进程刷写的缓冲区。
buffer-checkpoint-sync-start () 脏缓冲区已写入内核之后、开始发出 fsync 请求之前触发的探针。
buffer-checkpoint-done () 缓冲区到磁盘的同步完成时触发的探针。
twophase-checkpoint-start () 检查点的两阶段部分开始时触发的探针。
twophase-checkpoint-done () 检查点的两阶段部分完成时触发的探针。
buffer-read-start (ForkNumber, BlockNumber, Oid, Oid, Oid, bool, bool) 缓冲区读取开始时触发的探针。arg0 和 arg1 是页的分支号和块号(但如果是关系扩展请求,arg1 为 -1)。arg2、arg3 和 arg4 是标识该关系的表空间、数据库和关系 OID。arg5 对本地缓冲区为真,对共享缓冲区为假。arg6 对关系扩展请求为真,对普通读取为假。
buffer-read-done (ForkNumber, BlockNumber, Oid, Oid, Oid, bool, bool, bool) 缓冲区读取完成时触发的探针。arg0 和 arg1 是页的分支号和块号(如果是关系扩展请求,arg1 此时是新增块的块号)。arg2、arg3 和 arg4 是标识该关系的表空间、数据库和关系 OID。arg5 对本地缓冲区为真,对共享缓冲区为假。arg6 对关系扩展请求为真,对普通读取为假。arg7 如果缓冲区在池中找到则为真,否则为假。
buffer-flush-start (ForkNumber, BlockNumber, Oid, Oid, Oid) 对共享缓冲区发出任何写请求之前触发的探针。arg0 和 arg1 是页的分支号和块号。arg2、arg3 和 arg4 是标识该关系的表空间、数据库和关系 OID。
buffer-flush-done (ForkNumber, BlockNumber, Oid, Oid, Oid) 写请求完成时触发的探针。(注意这只是把数据传递给内核的时间;通常还没有真正写到磁盘上。)参数与 buffer-flush-start 相同。
buffer-write-dirty-start (ForkNumber, BlockNumber, Oid, Oid, Oid) 服务器进程开始写出脏缓冲区时触发的探针。(如果这种情况经常发生,说明shared_buffers太小或需要调整后台写控制参数。)arg0 和 arg1 是页的分支号和块号。arg2、arg3 和 arg4 是标识该关系的表空间、数据库和关系 OID。
buffer-write-dirty-done (ForkNumber, BlockNumber, Oid, Oid, Oid) 脏缓冲区写出完成时触发的探针。参数与 buffer-write-dirty-start 相同。
wal-buffer-write-dirty-start () 服务器进程因 WAL 缓冲区空间耗尽而开始写出脏 WAL 缓冲区时触发的探针。(如果这种情况经常发生,说明wal_buffers太小。)
wal-buffer-write-dirty-done () 脏 WAL 缓冲区写出完成时触发的探针。
xlog-insert (unsigned char, unsigned char) 插入 WAL 记录时触发的探针。arg0 是该记录的资源管理器(rmid)。arg1 是信息标志。
xlog-switch () 请求 WAL 段切换时触发的探针。
smgr-md-read-start (ForkNumber, BlockNumber, Oid, Oid, Oid) 开始从关系读取块时触发的探针。arg0 和 arg1 是页的分支号和块号。arg2、arg3 和 arg4 是标识该关系的表空间、数据库和关系 OID。
smgr-md-read-done (ForkNumber, BlockNumber, Oid, Oid, Oid, int, int) 块读取完成时触发的探针。arg0 和 arg1 是页的分支号和块号。arg2、arg3 和 arg4 是标识该关系的表空间、数据库和关系 OID。arg5 是实际读取的字节数,arg6 是请求的字节数(如果两者不同,则表明出现了问题)。
smgr-md-write-start (ForkNumber, BlockNumber, Oid, Oid, Oid) 开始向关系写入块时触发的探针。arg0 和 arg1 是页的分支号和块号。arg2、arg3 和 arg4 是标识该关系的表空间、数据库和关系 OID。
smgr-md-write-done (ForkNumber, BlockNumber, Oid, Oid, Oid, int, int) 块写入完成时触发的探针。arg0 和 arg1 是页的分支号和块号。arg2、arg3 和 arg4 是标识该关系的表空间、数据库和关系 OID。arg5 是实际写入的字节数,arg6 是请求的字节数(如果两者不同,则表明出现了问题)。
sort-start (int, bool, int, int, bool) 排序操作开始时触发的探针。arg0 指示堆排序、索引排序还是数据排序。arg1 为真表示强制唯一值。arg2 是键列数。arg3 是允许的工作内存 KB 数。arg4 为真表示需要随机访问排序结果。
sort-done (bool, long) 排序完成时触发的探针。arg0 为真表示外部排序,为假表示内部排序。arg1 是外部排序使用的磁盘块数,或内部排序使用的内存 KB 数。
lwlock-acquire (LWLockId, LWLockMode) 获取到一个 LWLock 时触发的探针。arg0 是该 LWLock 的 ID。arg1 是请求的锁模式,即排他或共享。
lwlock-release (LWLockId) 释放一个 LWLock 时触发的探针(但注意被释放的等待者尚未被唤醒)。arg0 是该 LWLock 的 ID。
lwlock-wait-start (LWLockId, LWLockMode) LWLock 不能立即获取、服务器进程开始等待该锁可用时触发的探针。arg0 是该 LWLock 的 ID。arg1 是请求的锁模式,即排他或共享。
lwlock-wait-done (LWLockId, LWLockMode) 服务器进程结束对 LWLock 的等待时触发的探针(它实际上尚未持有该锁)。arg0 是该 LWLock 的 ID。arg1 是请求的锁模式,即排他或共享。
lwlock-condacquire (LWLockId, LWLockMode) 调用者指定不等待却成功获取到 LWLock 时触发的探针。arg0 是该 LWLock 的 ID。arg1 是请求的锁模式,即排他或共享。
lwlock-condacquire-fail (LWLockId, LWLockMode) 调用者指定不等待且未能成功获取 LWLock 时触发的探针。arg0 是该 LWLock 的 ID。arg1 是请求的锁模式,即排他或共享。
lock-wait-start (unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, LOCKMODE) 重量级锁(lmgr 锁)请求因锁不可用而开始等待时触发的探针。arg0 到 arg3 是标识被锁定对象的标签字段。arg4 指示被锁定对象的类型。arg5 指示请求的锁类型。
lock-wait-done (unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, LOCKMODE) 重量级锁(lmgr 锁)请求结束等待时触发的探针(它实际上尚未持有该锁)。参数与 lock-wait-start 相同。
deadlock-found () 死锁检测器发现死锁时触发的探针。

表 27.4. 探针参数中使用的已定义类型

Type Definition
LocalTransactionId unsigned int
LWLockId int
LWLockMode int
LOCKMODE int
BlockNumber unsigned int
Oid unsigned int
ForkNumber int
bool char

27.4.3. 使用探针 #

下面的示例展示了一个分析系统中事务计数的 DTrace 脚本,可以用来代替一次性能测试之前和之后的pg_stat_database快照:

#!/usr/sbin/dtrace -qs

postgresql$1:::transaction-start
{
      @start["Start"] = count();
      self->ts  = timestamp;
}

postgresql$1:::transaction-abort
{
      @abort["Abort"] = count();
}

postgresql$1:::transaction-commit
/self->ts/
{
      @commit["Commit"] = count();
      @time["Total time (ns)"] = sum(timestamp - self->ts);
      self->ts=0;
}

当被执行时,该示例 D 脚本给出这样的输出:

# ./txn_count.d `pgrep -n postgres` or ./txn_count.d <PID>
^C

Start                                          71
Commit                                         70
Total time (ns)                        2312105013

注意

SystemTap 的追踪脚本记法与 DTrace 不同,但底层探针是兼容的。需要注意的一点是,截至本文编写时,SystemTap 脚本必须使用双下划线来代替连字符引用探针名。预计未来的 SystemTap 版本会修复这一点。

你应该记住,DTrace 脚本需要细心地编写和调试,否则被收集的追踪信息可能会毫无意义。在大多数发现问题的情况下,出错的是插桩,而不是底层系统。当讨论使用动态追踪发现的信息时,一定要附上使用的脚本以便其也被检查和讨论。

更多示例脚本可以在 PgFoundry 的dtrace 项目中找到。

27.4.4. 定义新探针 #

开发者可以在代码中任意位置定义新的探针,当然这要重新编译之后才能生效。下面是插入新探针的步骤:

  1. 决定探针名称以及要通过探针提供的数据

  2. 把该探针定义加入到src/backend/utils/probes.d

  3. 如果pg_trace.h尚未被包含该探针点的模块引用,则将它包含进来,并且在源代码中期望的位置插入TRACE_POSTGRESQL探针宏

  4. 重新编译并验证新探针是可用的

示例:.  这里是一个如何增加一个探针来用事务 ID 追踪所有新事务的示例。

  1. 决定探针将被命名为transaction-start并且需要一个LocalTransactionId类型的参数

  2. 将该探针定义加入到src/backend/utils/probes.d

    probe transaction__start(LocalTransactionId);
    

    注意探针名字中双下划线的使用。在一个使用探针的 DTrace 脚本中,双下划线需要被替换为一个连字符,因此,文档中应向用户说明的名称是transaction-start

  3. 在编译时,transaction__start被转换成一个名为TRACE_POSTGRESQL_TRANSACTION_START的宏(注意这里是单下划线),可以通过包含头文件pg_trace.h获得。将宏调用加入到源代码中的合适位置。在这种情况下,看起来类似:

    TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);
    
  4. 在重新编译和运行新的二进制文件之后,通过运行下面的 DTrace 命令来检查新增的探针是否可用。你应该看到类似下面的输出:

    # dtrace -ln transaction-start
       ID    PROVIDER          MODULE           FUNCTION NAME
    18705 postgresql49878     postgres     StartTransactionCommand transaction-start
    18755 postgresql49877     postgres     StartTransactionCommand transaction-start
    18805 postgresql49876     postgres     StartTransactionCommand transaction-start
    18855 postgresql49875     postgres     StartTransactionCommand transaction-start
    18986 postgresql49873     postgres     StartTransactionCommand transaction-start
    

向C代码中添加追踪宏时,有一些事情需要注意:

  • 需要小心的是,为探针参数指定的数据类型要匹配宏中使用的变量的数据类型,否则会发生编译错误。

  • 在大多数平台上,如果用--enable-dtrace编译了PostgreSQL,无论何时当控制经过一个追踪宏时,都会对该宏的参数求值,即使没有进行追踪也会这样做。如果只是报告少数局部变量的值,通常无需担心这一点。但是要注意不要将开销大的函数调用放入参数中。如果你需要这样做,考虑通过检查追踪是否真的被启用来保护该宏:

    if (TRACE_POSTGRESQL_TRANSACTION_START_ENABLED())
        TRACE_POSTGRESQL_TRANSACTION_START(some_function(...));
    

    每个追踪宏有一个对应的ENABLED宏。

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。