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 代码时,有一些需要注意的事项: