PostgreSQL 9.4.4 中文手册 | |||
---|---|---|---|
上一页 | 上一级 | 章 27. 监控数据库的活动 | 下一页 |
PostgreSQL允许对数据库服务器进行动态跟踪。 这样就允许在代码内特定的点上调用外部工具来跟踪执行过程。
许多跟踪点(也被称为"探头")已经插入在源代码中了, 这些探针的目的是被用于数据库开发者和管理员,默认情况下, 探头不编译成PostgreSQL; 用户必须运行配置脚本时明确启用它们。
目前,只有DTrace支持实用工具,在写这的时候, 它可在Solaris, OS X, FreeBSD, NetBSD和Oracle Linux上使用。 SystemTap 项目为Linux还提供了一个DTrace的等效并且也是可用的。 通过改变src/include/utils/probes.h 中的宏命令定义为支持其他的动态跟踪工具在理论上是可能的 。
跟踪点是默认禁止的,你必须明确告诉配置脚本以使得PostgreSQL中的探头可用。 使用--enable-dtrace选项来启用DTrace支持。 参见第 15.4 节获取更多信息。
表 27-16显示的是在源代码中提供的标准跟踪点, 表 27-17显示探测中使用的类型。 更多探测可以被添加以提高PostgreSQL的观测性。
表 27-16. 内置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包含了增加,删除和循环回收的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表示期望写的数目(arg1的buffer-sync-start); 任何的不同会导致另一个进程在检查点发生时刷新缓冲区。 |
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包含page块的锁和派生的子进程数(如果是一个关系扩展请求,arg1会是-1)。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 arg5是为局部缓冲创建临时关系时后端ID,或者共享缓冲区InvalidBackendId (-1)。 arg6对关系扩展请求表示真,对正常读表示假。 |
buffer-read-done | (ForkNumber, BlockNumber, Oid, Oid, Oid, int, bool, bool) | 当完成一次缓冲区读时触发的探测器。 arg0和arg1包含page块的锁和派生的子进程数 (如果是一个关系扩展请求,arg1会表示新增锁的数目)。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 arg5是为局部缓冲创建临时关系时后端ID,或者共享缓冲区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太小, 或需要调整bgwriter控制参数。 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表示记录的rm id。 arg1包含信息标志。 |
xlog-switch | () | 当要求进行WAL切换时触发的探测器。 |
smgr-md-read-start | (ForkNumber, BlockNumber, Oid, Oid, Oid, int) | 开始从一个关系中读取锁时触发的探测器。 arg0和arg1包含page块的锁和派生的子进程数。 arg2,arg3和arg4包含表空间,数据库和关系OID, 以识别关系。arg5是为局部缓冲创建临时关系时的后端ID,或者共享缓冲InvalidBackendId (-1) |
smgr-md-read-done | (ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) | 当一个锁读取完成时触发的探测器。 arg0和arg1包含page块的锁和派生的子进程数。 arg2,arg3和arg4包含表空间, 数据库和关系OID,以识别关系。 arg5是为局部缓冲创建临时关系时的后端ID,或者共享缓冲InvalidBackendId (-1), 而arg6表示实际读取的字节数,而arg7是要求数(如果不一样会报错) |
smgr-md-write-start | (ForkNumber, BlockNumber, Oid, Oid, Oid, int) | 当向一个关系中写入锁时触发的探测器。 arg0和arg1包含page块的锁和派生的子进程数。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 arg5是为局部缓冲创建临时关系时的后端ID,或者共享缓冲InvalidBackendId (-1)。 |
smgr-md-write-done | (ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) | 当一个锁写进程完成时触发的探测器。 arg0和arg1表示page块的锁和派生的子进程数。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 arg5表示为局部缓冲创建临时关系时的后端ID,或者共享缓冲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 *, int, LWLockMode) | 当成功获得一个LWLock时触发的探测器。 arg0是LWLock的一部分。arg1是LWLock部分的偏差。 arg2表明请求的锁模式,要么独占要么共享。 |
lwlock-release | (char *, int) | LWLock释放时触发的探测器 (但是请注意任何发布的等待者还未觉醒)。 arg0是LWLock的一部分。arg1是LWLock部分的偏差。 |
lwlock-wait-start | (char *, int, LWLockMode) | 当不能立即获得LWLock锁,同时服务进程进入等待时触发的探测器。 arg0是LWLock的一部分。arg1是LWLock部分的偏差。 arg2表明请求的锁模式,要么独占要么共享。 |
lwlock-wait-done | (char *, int, LWLockMode) | 当从一个LWLock锁中释放服务进程时触发的探测器(实际上没有进行锁)。 arg0是LWLock的一部分。arg1是LWLock部分的偏差。 arg2表明请求的锁模式,要么独占要么共享。 |
lwlock-condacquire | (char *, int, LWLockMode) | 当成功获得一个LWLock时触发的探测器(已声明调用无需等待)。 arg0是LWLock的一部分。arg1是LWLock部分的偏差。 arg2表明请求的锁模式,要么独占要么共享。 |
lwlock-condacquire-fail | (char *, int, LWLockMode) | 当没有成功获得一个LWLock时触发的探测器(已声明调用无需等待)。 arg0是LWLock的一部分。arg1是LWLock部分的偏差。 arg2表明请求的锁模式,要么独占要么共享。 |
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 | () | 当死锁探测器发现死锁时触发的探测器 |
下面的例子显示了一个分析事务次数的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 project 中找到。
开发者可以在代码中任意位置定义新的跟踪点, 当然这要重新编译之后才能生效。下面是用于新探测器插入步骤:
通过探头决定探头名字和可利用数据。
新增探头定义为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代码中添加跟踪宏时,有一些注意事项,见下文:
需要注意的是,为探测器参数声明的数据类型要匹配宏中可用的数据类型, 否则会发生编译错误。
在大多数平台上,如果编译PostgreSQL 时带有--enable-dtrace选项, 无论何时通过宏来控制时,都会估算该跟踪宏的参数, 即使没有进行跟踪。通常不需要担心是否你只是报告一些局部变量的值。 但要注意将重要的函数调用放置在参数中。如果需要这么做, 考虑通过检查是否真的开启跟踪来保护宏:
if (TRACE_POSTGRESQL_TRANSACTION_START_ENABLED()) TRACE_POSTGRESQL_TRANSACTION_START(some_function(...));
每个跟踪宏都有一个相应的ENABLED宏。