Postgresql 中文操作指南
28.5. Dynamic Tracing #
PostgreSQL 提供了支持数据库服务器动态跟踪的功能。这允许在代码中的特定点调用外部实用程序,从而跟踪执行。
一些探针或跟踪点已插入源代码。这些探针旨在供数据库开发人员和管理员使用。默认情况下,探针不会编译到 PostgreSQL 中;用户需要明确地告知 configure 脚本使探针可用。
目前,支持 DTrace实用程序,在撰写本文时,此实用程序可在 Solaris、macOS、FreeBSD、NetBSD 和 Oracle Linux 上使用。Linux 的 SystemTap项目提供了 DTrace 等效项,也可以使用。从理论上讲,通过更改 _src/include/utils/probes.h_中宏的定义,可以支持其他动态跟踪实用程序。
28.5.1. Compiling for Dynamic Tracing #
默认情况下,探测不可用,因此需要明确告知配置脚本在 PostgreSQL 中提供了探测。若要包括 DTrace 支持,请指定 _—enable-dtrace_以进行配置。有关详细信息,请参见 Section 17.3.3.6。
28.5.2. Built-in Probes #
如 Table 28.48中所示,源代码中提供了许多标准探测; Table 28.49显示了探测中使用的类型。肯定可以添加更多探测以增强 PostgreSQL 的可观察性。
Table 28.48. Built-in DTrace Probes
Name |
Parameters |
Description |
transaction-start |
(LocalTransactionId) |
在新事务启动时触发的探测。arg0 是事务 ID。 |
transaction-commit |
(LocalTransactionId) |
当事务成功完成时触发的探测。arg0 是事务 ID。 |
transaction-abort |
(LocalTransactionId) |
当事务不成功完成时触发的探测。arg0 是事务 ID。 |
query-start |
(const char *) |
当开始处理查询时触发的探测。arg0 是查询字符串。 |
query-done |
(const char *) |
当查询处理完成时触发的探测。arg0 是查询字符串。 |
query-parse-start |
(const char *) |
当开始解析查询时触发的探测。arg0 是查询字符串。 |
query-parse-done |
(const char *) |
当查询解析完成时触发的探测。arg0 是查询字符串。 |
query-rewrite-start |
(const char *) |
当开始重写查询时触发的探测。arg0 是查询字符串。 |
query-rewrite-done |
(const char *) |
当查询重写完成时触发的探测。arg0 是查询字符串。 |
query-plan-start |
() |
当开始计划查询时触发的探测。 |
query-plan-done |
() |
当查询计划完成时触发的探测。 |
query-execute-start |
() |
当开始执行查询时触发的探测。 |
query-execute-done |
() |
当查询执行完成时触发的探测。 |
statement-status |
(const char *) |
当服务器进程更新其 pg_stat_activity . status 时触发的探测。arg0 是新的状态字符串。 |
checkpoint-start |
(int) |
当检查点启动时触发的探测。arg0 保存用于区分不同检查点类型(如关闭、立即或强制)的位掩码标志。 |
checkpoint-done |
(int, int, int, int, int) |
当检查点完成时触发的探测。(下面列出的探测在检查点处理过程中按顺序触发。)arg0 是写入的缓冲区数。arg1 是缓冲区的总数。arg2、arg3 和 arg4 分别包含已添加、已删除和已回收的 WAL 文件数量。 |
clog-checkpoint-start |
(bool) |
在检查点的 CLOG 部分启动时触发的探测器。对于正常检查点,arg0 为 true,对于关闭检查点,arg0 为 false。 |
clog-checkpoint-done |
(bool) |
在检查点的 CLOG 部分完成时触发的探测器。arg0 的含义与 clog-checkpoint-start 相同。 |
subtrans-checkpoint-start |
(bool) |
在检查点的 SUBTRANS 部分启动时触发的探测器。对于正常检查点,arg0 为 true,对于关闭检查点,arg0 为 false。 |
subtrans-checkpoint-done |
(bool) |
在检查点的 SUBTRANS 部分完成时触发的探测器。arg0 的含义与 subtrans-checkpoint-start 相同。 |
multixact-checkpoint-start |
(bool) |
在检查点的 MultiXact 部分启动时触发的探测器。对于正常检查点,arg0 为 true,对于关闭检查点,arg0 为 false。 |
multixact-checkpoint-done |
(bool) |
在检查点的 MultiXact 部分完成时触发的探测器。arg0 的含义与 multixact-checkpoint-start 相同。 |
buffer-checkpoint-start |
(int) |
在检查点的缓冲区写入部分启动时触发的探测器。arg0 保留用于区分不同检查点类型的按位标志,例如关闭、立即或强制。 |
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-extend-start |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int, unsigned int) |
在关系扩展启动时触发的探测器。arg0 包含要扩展的分叉。arg1、arg2 和 arg3 包含表空间、数据库和关系 OID,用于标识该关系。arg4 是为本地缓冲区创建临时关系的后端 ID,或 InvalidBackendId (-1)表示共享缓冲区。arg5 是调用方想要扩展的块数。 |
buffer-extend-done |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int, unsigned int, BlockNumber) |
在关系扩展完成后触发的探测器。arg0 包含要扩展的分叉。arg1、arg2 和 arg3 包含表空间、数据库和关系 OID,用于标识该关系。arg4 是为本地缓冲区创建临时关系的后端 ID,或 InvalidBackendId (-1)表示共享缓冲区。arg5 是关系已扩展的块数,由于资源限制,它可能小于 buffer-extend-start 中的数字。arg6 包含第一个新块的块编号。 |
buffer-read-start |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int) |
缓冲区读开始时激发的探测。arg0 和 arg1 包含页面的分叉和块号。arg2、arg3 和 arg4 包含标识关系的表空间、数据库和关系 OID。arg5 是创建临时关系用于本地缓冲区的后端 ID,或用于共享缓冲区的 InvalidBackendId (-1)。 |
buffer-read-done |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int, bool) |
缓冲区读完成时激发的探测。arg0 和 arg1 包含页面的分叉和块号。arg2、arg3 和 arg4 包含标识关系的表空间、数据库和关系 OID。arg5 是创建临时关系用于本地缓冲区的后端 ID,或用于共享缓冲区的 InvalidBackendId (-1)。如果在池中找到缓冲区,arg6 为 true;如果没有找到,则为 false。 |
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 相同。 |
wal-buffer-write-dirty-start |
() |
当服务器进程开始写入脏 WAL 缓冲区是因为没有更多 WAL 缓冲区空间可用时激发的探测。(如果这种情况经常发生,则表示 wal_buffers 太小。) |
wal-buffer-write-dirty-done |
() |
脏 WAL 缓冲区写入完成时激发的探测。 |
wal-insert |
(unsigned char, unsigned char) |
WAL 记录插入时激发的探测。arg0 是记录的资源管理器 (rmid)。arg1 包含信息标志。 |
wal-switch |
() |
WAL 段切换请求时激发的探测。 |
smgr-md-read-start |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int) |
开始从关系读取块时激发的探测。arg0 和 arg1 包含页面的分叉和块号。arg2、arg3 和 arg4 包含标识关系的表空间、数据库和关系 OID。arg5 是创建临时关系用于本地缓冲区的后端 ID,或用于共享缓冲区的 InvalidBackendId (-1)。 |
smgr-md-read-done |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) |
块读取完成时激发的探测。arg0 和 arg1 包含页面的分叉和块号。arg2、arg3 和 arg4 包含标识关系的表空间、数据库和关系 OID。arg5 是创建临时关系用于本地缓冲区的后端 ID,或用于共享缓冲区的 InvalidBackendId (-1)。arg6 是实际读取的字节数,而 arg7 是请求的字节数(如果这两个数字不同则表示有麻烦)。 |
smgr-md-write-start |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int) |
将块写入关系开始时激发的探测。arg0 和 arg1 包含页面的分叉和块号。arg2、arg3 和 arg4 包含标识关系的表空间、数据库和关系 OID。arg5 是创建临时关系用于本地缓冲区的后端 ID,或用于共享缓冲区的 InvalidBackendId (-1)。 |
smgr-md-write-done |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) |
块写入完成时激发的探测。arg0 和 arg1 包含页面的分叉和块号。arg2、arg3 和 arg4 包含标识关系的表空间、数据库和关系 OID。arg5 是创建临时关系用于本地缓冲区的后端 ID,或用于共享缓冲区的 InvalidBackendId (-1)。arg6 是实际写入的字节数,而 arg7 是请求的字节数(如果这两个数字不同则表示有麻烦)。 |
sort-start |
(int, bool, int, int, bool, int) |
排序操作开始时激发的探测。arg0 指示堆、索引或基准数据排序。arg1 对于唯一值强制为 true。arg2 是关键列的数量。arg3 是允许的工作内存千字节数。如果需要对排序结果进行随机访问,则 arg4 为 true。在 0 时指示串行,在 1 时指示并行工作者,在 2 时指示并行领导者。 |
sort-done |
(bool, long) |
排序完成时激发的探测。arg0 对于外部排序为 true,对于内部排序为 false。arg1 是用于外部排序的磁盘块数,或用于内部排序的内存千字节数。 |
lwlock-acquire |
(char *, LWLockMode) |
LWLock 被获取时激发的探测。arg0 是 LWLock 的批次。arg1 是请求的锁定模式,可以是独占或共享。 |
lwlock-release |
(char *) |
当 LWLock 被释放时触发的探测(但请注意,尚未唤醒任何已释放的等待器)。arg0 是 LWLock 的批次。 |
lwlock-wait-start |
(char *, LWLockMode) |
当 LWLock 未立即可用,且服务器进程已开始等待锁变为可用时触发的探测。arg0 是 LWLock 的批次。arg1 是请求的锁模式,为排他锁或共享锁。 |
lwlock-wait-done |
(char *, LWLockMode) |
服务器进程已从其等待 LWLock 时释放时触发的探针(它实际上还没有获取锁)。arg0 是 LWLock 的分区。arg1 是请求的锁模式,可以是独占或共享。 |
lwlock-condacquire |
(char *, LWLockMode) |
当调用者指定不等待时成功获取 LWLock 时触发的探针。arg0 是 LWLock 的分区。arg1 是请求的锁模式,可以是独占或共享。 |
lwlock-condacquire-fail |
(char *, LWLockMode) |
当调用者指定不等待时不成功获取 LWLock 时触发的探针。arg0 是 LWLock 的分区。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 |
() |
当死锁检测器发现死锁时触发的探针。 |
Table 28.49. Defined Types Used in Probe Parameters
Type |
Definition |
LocalTransactionId |
unsigned int |
LWLockMode |
int |
LOCKMODE |
int |
BlockNumber |
unsigned int |
Oid |
unsigned int |
ForkNumber |
int |
bool |
unsigned char |
28.5.3. Using Probes #
以下示例显示了一个 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
Note
SystemTap 使用与 DTrace 不同的追踪脚本表示法,即便底层追踪点兼容。一点值得注意的是,在撰写本文时,SystemTap 脚本必须使用双下划线来引用探针名称,以代替连字符。预计此问题在 SystemTap 的未来版本中会得到解决。
请记住,必须仔细编写和调试 DTrace 脚本,否则收集的跟踪信息可能毫无意义。在大多数发现问题的情况下,出现故障的是检测,而不是底层系统。当讨论使用动态跟踪找到的信息时,请务必附上用于允许检查和讨论的信息的脚本。
28.5.4. Defining New Probes #
可以根据开发人员的需要在代码中定义新的探针,但这需要重新编译。以下是插入新探针的步骤:
*Example: * 这是一个示例,介绍如何添加探针来按事务 ID 跟踪所有新事务。
在将跟踪宏添加到 C 代码时,有一些需要注意的事项: