©
This document uses PHP Chinese website manual Release
PostgreSQL允许对数据库服务器进行动态跟踪。这样就允许在代码内特 定的点上调用外部工具来跟踪执行过程。目前此功能主要目的是为了提供给数据库开发者使用,它要求使用者对代码非常熟悉。
许多跟踪点(也被称为"探头")已经插入在源代码中了,这些探针的目的是被用于 数据库开发者管理员,默认情况下,探头不编译成 PostgreSQL; 用户必须在编译前运行configure脚本时明确启用它们。
目前,只有 DTrace 支持实用工具,它可 在OpenSolaris,Solaris10操作系统,和MacOSXLeopard的。据预计, DTrace将可以在FreeBSD上的未来和其他可能的 操作系统。“ SystemTap项目 Linux还提供了一个DTrace的等效。支持其他动态, 通过改变src/include/utils/probes.h中的定义为跟踪实用程序在理论上是可能的 。
跟踪点是默认禁止的,你必须在运行configure脚本在PostgreSQL时明确使 用--enable-dtrace选项来启用DTrace支持。参见Section 15.5获取更多信息。
Table 27-3显示的是在源代码中提 供的标准跟踪点,此外,也可以根据特定的具体问题添加其它跟踪点。 更多可以肯定会增加,以提高PostgreSQL的 观测性。
Table 27-3. 内置跟踪点
名字 | 参数 | 概述 |
---|---|---|
transaction-start | (LocalTransactionId) | 开始新事务,arg0是事务ID。 |
transaction-commit | (LocalTransactionId) | 事务成功完成,arg0是事务ID。 |
transaction-abort | (LocalTransactionId) | 当交易完成失败将触发的探测器。 arg0是事务ID。 |
query-start | (constchar*) | 开始处理查询时将触发的探测器。 arg0是查询字符串。 |
query-done | (constchar*) | 查询处理完成时将触发的探测器。 arg0是查询字符串。 |
query-parse-start | (constchar*) | 解析查询开始时将触发的探测器。 arg0是查询字符串。 |
query-parse-done | (constchar*) | 完整解析查询时触发的探测器。 arg0是查询字符串。 |
query-rewrite-start | (constchar*) | 启动触发的探测器时,查询重写。 arg0是查询字符串。 |
query-rewrite-done | (constchar*) | 探头触发时,查询重写是完整的。 arg0是查询字符串。 |
query-plan-start | () | 查询规划开始时将触发的探测器 |
query-plan-done | () | 查询规划完成时将触发的探测器 |
query-execute-start | () | 执行规划开始时将触发的探测器 |
query-execute-done | () | 执行规划完成时将触发的探测器 |
statement-status | (constchar*) | 服务进程随时更新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表示期望写的数目(arg1 of buffer-sync-start); 任何的不同会导致另一个进程在检查点发生时刷写缓冲区。 |
buffer-checkpoint-sync-start | () | 当完成将脏缓冲区写入到内核,并且还没有发出fsync请求之前触发的探测器。 |
buffer-checkpoint-done | () | 当同步缓冲区到磁盘完成时触发的探测器。 |
twophase-checkpoint-start | () | 当一个检查点的两相阶段状态部分开始时触发的探测器。 |
twophase-checkpoint-done | () | 当一个检查点的两相阶段状态部分完成时触发的探测器。 |
buffer-read-start | (ForkNumber,BlockNumber,O id,O id,O id,bool,bool) | 当开始一次缓冲区读时触发的探测器。 arg0和arg1包含page块中锁和派生的子进程数(如果是一个关系扩展请求,arg1会是-1)。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 arg5对本地缓冲区表示真,对共享缓冲区表示假。 arg6对关系扩展请求表示真,对正常读表示假。 |
buffer-read-done | (ForkNumber,BlockNumber,O id,O id,O id,bool,bool,bool) | 当完成一次缓冲区读时触发的探测器。 arg0和arg1包含page块中锁和派生的子进程数(如果是一个关系扩展请求,arg1会表示新增锁的数目)。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 arg5对本地缓冲区表示真,对共享缓冲区表示假。 arg6对关系扩展请求表示真,对正常读表示假。 如果池中有缓冲区,则arg7表示真,反之表示假。 |
buffer-flush-start | (ForkNumber,BlockNumber,O id,O id,O id) | 在发出共享缓冲区的任意写入请求时触发的探测器。 arg0和arg1包含page块中锁和派生的子进程数。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 |
buffer-flush-done | (ForkNumber,BlockNumber,O id,O id,O id) | 当完成一条写要求时触发的探测器。 需要注意的是,它只影响将数据传递到内核参数的时间; 实际上,它不会写到磁盘上。这个参数与buffer-flush-start一致。 |
buffer-write-dirty-start | (ForkNumber,BlockNumber,O id,O id,O id) | 当服务器进程开始写脏缓冲区时触发的探测电器。如果经常发生,表示shared_buffers太小 ,或需要调整bgwriter控制参数。 arg0和arg1包含page块中锁和派生的子进程数。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 |
buffer-write-dirty-done | (ForkNumber,BlockNumber,O id,O id,O id) | 当完成脏缓冲区写时触发的探测器。 参数与buffer-write-dirty-start一样。 |
wal-buffer-write-dirty-start | () | 当服务器进程开始写脏读时触发的探测器(此时WAL缓冲区已满)。 如果经常发生,应该是wal_buffers设置的太小了。 |
wal-buffer-write-dirty-done | () | 当完成一次WAL脏写时触发的探测器。 |
xlog-insert | (unsignedchar,unsignedchar) | 当插入一条WAL记录时触发的探测器。 arg0表示记录的rm id。 arg1包含信息标志。 |
xlog-switch | () | 当要求进行WAL切换时触发的探测器。 |
smgr-md-read-start | (ForkNumber,BlockNumber,O id,O id,O id) | 开始从一个关系中写锁时触发的探测器。 arg0和arg1包含page块中锁和派生的子进程数。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 |
smgr-md-read-done | (ForkNumber,BlockNumber,O id,O id,O id,int,int) | 当一个锁写完成时触发的探测器。 arg0和arg1包含page块中锁和派生的子进程数。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 arg5表示实际读的字节数,而arg6表示要求的字节数(如果不一样会报错)。 |
smgr-md-write-start | (ForkNumber,BlockNumber,O id,O id,O id) | 当向一个关系中写入锁时触发的探测器。 arg0和arg1包含page块中锁和派生子进程数。 arg2,arg3和arg4包含表空间,数据库和关系OID,以识别关系。 |
smgr-md-write-done | (ForkNumber,BlockNumber,O id,O id,O id,int,int) | 当一个锁写进程完成时触发的探测器。 arg0和arg1表示page块的锁和派生的子进程数。 arg2,arg3和arg4包含表空间,数据库和关系OID。 arg5表示实际写的字节数,而arg6表示要求的数目(如果不一样会报错)。 |
sort-start | (int,bool,int,int,bool) | 排序操作开始时触发的探测器。 arg1对强制唯一值表示真。 arg2表示键列的数目。 arg3表示允许使用的内存数目(以千字节为单位)。 如果要求随机访问排序结果,那么arg4表示真。 |
sort-done | (bool,long) | 排序操作结束时触发的探测器。 arg0对外部排序表示真,内部排序表示假。 arg1表示用于一个外部排序的磁盘锁的数目,或用于一个内部排序的,以千字节为单位的内存数目。 |
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 | (unsignedint,unsignedint,unsignedint,unsignedint,unsignedint,LOCKMODE) | 当一个重量级锁(lmgr锁)的请求开始等待(因为无法获得锁)时触发的探测器。 arg0到arg3是辨别被锁定对象的标签字段。arg4指出被锁对象的类型。 arg5表示请求的锁类型。 |
lock-wait-done | (unsignedint,unsignedint,unsignedint,unsignedint,unsignedint,LOCKMODE) | 当一个重量级锁(lmgr锁)的请求结束等待时触发的探测器,参数与lock-wait-start一样。 |
deadlock-found | () | 当死锁探测器发现死锁是触发的探测器 |
Table 27-4. 定义用于探测器参数的类型
类型 | 定义 |
---|---|
LocalTransactionId | unsignedint |
LWLockId | int |
LWLockMode | int |
LOCKMODE | int |
BlockNumber | unsignedint |
O id | unsignedint |
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["Totaltime(ns)"]=sum(timestamp-self->ts); self->ts=0; }
例如示范D脚本执行时,如输出:
#./txn_count.d`pgrep-npostgres`or./txn_count.d<PID> ^C Start71 Commit70 Totaltime(ns)2312105013
Note: SystemTap为跟踪脚本使用一个不同的标记而不是Dtrace,即使底层的跟踪点是兼容的。 有一点需要注意,在这样写的时候,SystemTap脚本必须使用双下划线代替连字符来指向探测器名。
必须在实际使用跟踪程序前进行仔细的编写和充分的调试,否则收集到的跟踪信息可能毫 无意义。大多数问题是由于外部跟踪程序错误导致的而不是底层系统。在讨论使 用动态跟踪发现的信息时,应确保在其中包含你使用的跟踪脚本。
更多的示例脚本,可以发现在PgFoundrydtraceproject.
开发者可以在代码中任意位置定义新的跟踪点,当然这要重新编译之后才能生效。下面是 用于新探测器插入步骤:
通过探头决定探头名字和数据可利用。
新增探头定义为src/backend/utils/probes.d
包括pg_trace.h,如果已经不在模块中包含探测点,并且在 所需源代码中期望的位置插入TRACE_POSTGRESQL探测宏。
重新编译和验证,新探头是可用的
Example: 下面是一个例子,你将如何添加一个探头,追踪所有新的 交易通过事务ID。
决定,探测器将被命名为transaction-start并且 需要LocalTransactionId类型参数
新增探头定义为src/backend/utils/probes.d
probetransaction__start(LocalTransactionId);
注意在探测器名字中的双下划线。在一个DTrace使用探测器,需要用一个连字符来替换双下滑线,因此 ,对用户而言,transaction-start是文档名。
在编译时,transaction__start被转换成一个宏调用TRACE_POSTGRESQL_TRANSACTION_START (注意这里是单下划线),可以从pg_trace.h中获得。将宏调用放在源代码中的合适位置。 在这种情况下,类似下面:
TRACE_POSTGRESQL_TRANSACTION_START(vx id.localTransactionId);
在重新编译和运行新的二进制文件之后,通过运行下面的DTrace命令来检查新增的探测器是否可用。 应该得到类似下面的结果:
#dtrace-lntransaction-start IDPROVIDERMODULEFUNCTIONNAME 18705postgresql49878postgresStartTransactionCommandtransaction-start 18755postgresql49877postgresStartTransactionCommandtransaction-start 18805postgresql49876postgresStartTransactionCommandtransaction-start 18855postgresql49875postgresStartTransactionCommandtransaction-start 18986postgresql49873postgresStartTransactionCommandtransaction-start
向C代码中添加跟踪宏时,有一些注意事项,见下文。
需要注意的是,为探测器参数声明的数据类型要匹配宏中可用的数据类型,否则会发生编译错误。
在大多数平台上,如果编译PostgreSQL时带有--enable-dtrace选项, 无论何时通过宏来控制时,都会估算该跟踪宏的参数,即使没有进行跟踪。通常不需要担心是否你只是报告一些局部变量的值。 但要注意将重要的函数调用放置在参数中。如果需要这么做,考虑通过检查跟踪是否真的开启的检查来保护宏:
if(TRACE_POSTGRESQL_TRANSACTION_START_ENABLED()) TRACE_POSTGRESQL_TRANSACTION_START(some_function(...));
每个跟踪宏都有一个相应的ENABLED宏。