pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。
PostgreSQL提供了功能来支持数据库服务器的动态追踪。这样就允许在代码中的特 定点上调用外部工具来追踪执行过程。
一些探针或追踪点已经被插入在源代码中。这些探针的目的是被数据库开发者和管理员使用。默认情况下,探针不被编译到PostgreSQL中;用户需要显式地告诉配置脚本使得探针可用。
Currently, only the DTrace 工具支持,它可用于 OpenSolaris、Solaris 10 和 Mac OS X Leopard。预计 DTrace 将来也会在 FreeBSD 以及可能的其他操作系统上可用。Linux 上的SystemTap项目也提供了 DTrace 的等价物。通过修改src/include/utils/probes.h中宏的定义,理论上也可以支持其他动态追踪工具。
默认情况下,探针是不可用的,因此你将需要显式地告诉配置脚本让探针在PostgreSQL中可用。要包括 DTrace 支持,在配置时指定--enable-dtrace。更多信息请见第 15.5 节。
源码中提供了许多标准探针,如表 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 |
下面的示例展示了一个分析系统中事务计数的 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 项目中找到。
开发者可以在代码中任意位置定义新的探针,当然这要重新编译之后才能生效。下面是插入新探针的步骤:
决定探针名称以及要通过探针提供的数据
把该探针定义加入到src/backend/utils/probes.d
如果pg_trace.h尚未被包含该探针点的模块引用,则将它包含进来,并且在源代码中期望的位置插入TRACE_POSTGRESQL探针宏
重新编译并验证新探针是可用的
示例:. 这里是一个如何增加一个探针来用事务 ID 追踪所有新事务的示例。
决定探针将被命名为transaction-start并且需要一个LocalTransactionId类型的参数
将该探针定义加入到src/backend/utils/probes.d:
probe transaction__start(LocalTransactionId);
注意探针名字中双下划线的使用。在一个使用探针的 DTrace 脚本中,双下划线需要被替换为一个连字符,因此,文档中应向用户说明的名称是transaction-start。
在编译时,transaction__start被转换成一个名为TRACE_POSTGRESQL_TRANSACTION_START的宏(注意这里是单下划线),可以通过包含头文件pg_trace.h获得。将宏调用加入到源代码中的合适位置。在这种情况下,看起来类似:
TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);
在重新编译和运行新的二进制文件之后,通过运行下面的 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 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。