PostgreSQL 10.1 手册_部分 III. 服务器管理_第 28 章 监控数据库活动_28.5. 动态追踪

本文涉及的产品
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
云原生数据库 PolarDB PostgreSQL 版,标准版 2核4GB 50GB
简介: 28.5. 动态追踪 28.5.1. 动态追踪的编译 28.5.2. 内建探针 28.5.3. 使用探针 28.5.4. 定义新探针 PostgreSQL提供了功能来支持数据库服务器的动态追踪。

28.5. 动态追踪

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

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

目前,在写本文当时DTrace已被支持,它在 Solaris、macOS、FreeBSD、NetBSD 和 Oracle Linux 上可用。Linux 的SystemTap项目提供了一种可用的 DTrace 等价物。支持其他动态追踪工具在理论上可以通过改变src/include/utils/probes.h中的宏定义实现。

28.5.1. 动态追踪的编译

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

28.5.2. 内建探针

表 28.23所示,源代码中提供了一些标准探针。表 28.24显式了在探针中使用的类型。当然,可以增加更多探针来增强PostgreSQL的可观测性。

表 28.23. 内建 DTrace 探针

名称 参数 描述
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 保持逐位标志来区分不同的检查点类型,例如关闭(shutdown)、立即(immediate)或强制(force)。
checkpoint-done (int, int, int, int, int) 当一个检查点完成时触发的探针(检查点处理过程中序列中列出的下一个触发的探针)。arg0 是要写的缓冲区数量。arg1 是缓冲区的总数。arg2、arg3 和 arg4 分别包含了增加、删除和循环回收的 WAL 文件的数量。
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);arg1 和 arg2 的任何的不同反映在该检查点期间有其他进程刷写了缓冲区。
buffer-checkpoint-sync-start () 在脏缓冲区被写入到内核之后并且在开始发出 fsync 请求之前触发的探针。
buffer-checkpoint-done () 当同步缓冲区到磁盘完成时触发的探针。
twophase-checkpoint-start () 当一个检查点的两阶段部分被开始时触发的探针。
twophase-checkpoint-done () 当一个检查点的两阶段部分完成时触发的探针。
buffer-read-start (ForkNumber, BlockNumber, Oid, Oid, Oid, int, bool) 当一次缓冲区读被开始时触发的探针。arg0 和 arg1 包含该页的分叉号和块号(如果这是一次关系扩展请求,arg1 为 -1)。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。对一个本地缓冲区,arg5 是创建临时关系的后端的 ID;对于一个共享缓冲区,arg5 是 InvalidBackendId(-1)。表示真,对共享缓冲区表示假。 arg6 为真表示一次关系扩展请求,为假表示正常读。
buffer-read-done (ForkNumber, BlockNumber, Oid, Oid, Oid, int, bool, bool) 当一次缓冲区读完成时触发的探测器。arg0 和 arg1 包含该页的分叉号和块号(如果这是一次关系扩展请求,arg1 现在包含新增加块的块号)。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。对一个本地缓冲区,arg5 是创建临时关系的后端的 ID;对于一个共享缓冲区,arg5 是 InvalidBackendId(-1)。表示真,对共享缓冲区表示假。 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 缓冲区完成时触发的探针。
wal-insert (unsigned char, unsigned char) 当一个 WAL 记录被插入时触发的探针。arg0 是该记录的资源管理者(rmid)。arg1 包含 info 标志。
wal-switch () 当请求一次 WAL 段切换时触发的探针。
smgr-md-read-start (ForkNumber, BlockNumber, Oid, Oid, Oid, int) 当开始从一个关系读取一块时触发的探针。arg0 和 arg1 包含该页的分叉号和块号。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。对一个本地缓冲区,arg5 是创建临时关系的后端的 ID;对于一个共享缓冲区,arg5 是InvalidBackendId(-1)。
smgr-md-read-done (ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) 当一次块读取完成时触发的探针。arg0 和 arg1 包含该页的分叉号和块号。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。对一个本地缓冲区,arg5 是创建临时关系的后端的 ID;对于一个共享缓冲区,arg5 是InvalidBackendId(-1)。arg6 是实际读取的字节数,而 arg7 是请求读取的字节数(如果两者不同就意味着麻烦)。
smgr-md-write-start (ForkNumber, BlockNumber, Oid, Oid, Oid, int) 当开始向一个关系中写入一个块时触发的探针。arg0 和 arg1 包含该页的分叉号和块号。arg2、arg3 和 arg4 包含表空间、数据库和关系 OID 用以识别该关系。对一个本地缓冲区,arg5 是创建临时关系的后端的 ID;对于一个共享缓冲区,arg5 是InvalidBackendId(-1)。
smgr-md-write-done (ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) 当一个块写操作完成时触发的探针。arg0 和 arg1 包含该页的分叉号和块号。arg2、arg3和arg4 包含表空间、数据库和关系 OID来标识该关系。对于一个本地缓冲区,arg5 是创建临时关系的后端 ID;对于一个共享缓冲区,arg5 是InvalidBackendId(-1)。arg6 是实际写的字节数,而 arg7 是要求写的字节数(如果这两者不同,则意味着麻烦)。
sort-start (int, bool, int, int, bool) 当一次排序操作开始时触发的探针。arg0 指示是堆排序、索引排序或数据排序。arg1 为真表示唯一值强制。arg2 是键列的数目。arg3 是允许使用的工作内存数(以千字节计)。如果要求随机访问排序结果,那么 arg4 为真。
sort-done (bool, long) 当一次排序完成时触发的探针。arg0 为真表示外排序,为假表示内排序。arg1 是用于一次外排序的磁盘块的数目,或用于一次内排序的以千字节计的内存。
lwlock-acquire (char *, LWLockMode) 当成功获得一个 LWLock 时触发的探针。 arg0 是该 LWLock 所在的切片(Tranche)。 arg1 是所请求的锁模式,是排他或共享。
lwlock-release (char *) 当一个 LWLock 被释放时(但是注意还没有唤醒任何一个被释放的等待者)触发的探针。 arg0 是该 LWLock 所在的切片(Tranche)。
lwlock-wait-start (char *, LWLockMode) 当一个 LWLock不是当即可用并且一个服务器进程因此开始等待该锁变为可用时触发的探针。 arg0 是该 LWLock 所在的切片(Tranche)。 arg1 是所请求的锁模式,是排他或共享。
lwlock-wait-done (char *, LWLockMode) 当一个进程从对一个 LWLock 的等待中被释放时(它实际还没有得到该锁)时触发的探针。arg0 是该 LWLock 所在的切片(Tranche)。 arg1 是所请求的锁模式,是排他或共享。
lwlock-condacquire (char *, LWLockMode) 当调用者指定无需等待而成功获得一个 LWLock 时触发的探针。arg0 是该 LWLock 所在的切片(Tranche)。 arg1 是所请求的锁模式,是排他或共享。
lwlock-condacquire-fail (char *, LWLockMode) 当调用者指定无需等待而没有成功获得一个 LWLock 时触发的探针。arg0 是该 LWLock 所在的切片(Tranche)。 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 () 当死锁检测器发现死锁时触发的探针。

表 28.24. 定义用在探针参数中的类型

类型 定义
LocalTransactionId unsigned int
LWLockMode int
LOCKMODE int
BlockNumber unsigned int
Oid unsigned int
ForkNumber int
bool char

28.5.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 脚本需要细心地编写和调试,否则被收集的追踪信息可能会毫无意义。在大部分发现问题的情况中,它就是发生问题的部件,而不是底层系统。当讨论使用动态追踪发现的信息时,一定要封闭使用的脚本来允许这些以便被检查和讨论。

28.5.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宏。

本文转自PostgreSQL中文社区,原文链接: 28.5. 动态追踪
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
20天前
|
存储 关系型数据库 数据库
【赵渝强老师】PostgreSQL的数据库
PostgreSQL的逻辑存储结构涵盖数据库集群、数据库、表、索引、视图等对象,每个对象有唯一的oid标识。数据库集群包含多个数据库,每个数据库又包含多个模式,模式内含表、函数等。通过特定SQL命令可查看和管理这些数据库对象。
|
2月前
|
存储 关系型数据库 MySQL
一个项目用5款数据库?MySQL、PostgreSQL、ClickHouse、MongoDB区别,适用场景
一个项目用5款数据库?MySQL、PostgreSQL、ClickHouse、MongoDB——特点、性能、扩展性、安全性、适用场景比较
|
22天前
|
存储 关系型数据库 数据库
【赵渝强老师】PostgreSQL的数据库集群
PostgreSQL的逻辑存储结构涵盖了数据库集群、数据库、表、索引、视图等对象,每个对象都有唯一的oid标识。数据库集群是由单个PostgreSQL实例管理的所有数据库集合,共享同一配置和资源。集群的数据存储在一个称为数据目录的单一目录中,可通过-D选项或PGDATA环境变量指定。
|
1月前
|
关系型数据库 分布式数据库 数据库
PostgreSQL+Citus分布式数据库
PostgreSQL+Citus分布式数据库
62 15
|
3月前
|
SQL 关系型数据库 数据库
PostgreSQL数据库报错 ERROR: multiple default values specified for column "" of table "" 如何解决?
PostgreSQL数据库报错 ERROR: multiple default values specified for column "" of table "" 如何解决?
359 59
|
1月前
|
SQL 关系型数据库 数据库
PostgreSQL性能飙升的秘密:这几个调优技巧让你的数据库查询速度翻倍!
【10月更文挑战第25天】本文介绍了几种有效提升 PostgreSQL 数据库查询效率的方法,包括索引优化、查询优化、配置优化和硬件优化。通过合理设计索引、编写高效 SQL 查询、调整配置参数和选择合适硬件,可以显著提高数据库性能。
282 1
|
1月前
|
存储 关系型数据库 MySQL
MySQL vs. PostgreSQL:选择适合你的开源数据库
在众多开源数据库中,MySQL和PostgreSQL无疑是最受欢迎的两个。它们都有着强大的功能、广泛的社区支持和丰富的生态系统。然而,它们在设计理念、性能特点、功能特性等方面存在着显著的差异。本文将从这三个方面对MySQL和PostgreSQL进行比较,以帮助您选择更适合您需求的开源数据库。
156 4
|
2月前
|
SQL 关系型数据库 数据库
使用 PostgreSQL 和 Python 实现数据库操作
【10月更文挑战第2天】使用 PostgreSQL 和 Python 实现数据库操作
|
3月前
|
Oracle NoSQL 关系型数据库
主流数据库对比:MySQL、PostgreSQL、Oracle和Redis的优缺点分析
主流数据库对比:MySQL、PostgreSQL、Oracle和Redis的优缺点分析
588 2
|
2月前
|
存储 关系型数据库 MySQL
四种数据库对比MySQL、PostgreSQL、ClickHouse、MongoDB——特点、性能、扩展性、安全性、适用场景
四种数据库对比 MySQL、PostgreSQL、ClickHouse、MongoDB——特点、性能、扩展性、安全性、适用场景