PostgreSQL 允许对数据库服务器进行动态跟踪。这样就允许在代码内特定的点上调用外部工具来跟踪执行过程。目前此功能主要目的是为了提供给数据库开发者使用,它要求使用者对代码非常熟悉。
许多跟踪点(也被称为"探头")已经插入在源代码中了,不过缺省为禁用状态,用户必须在编译前运行 configure 脚本时明确启用它们。
目前仅支持 Solaris Express 和 Solaris 10+ 系统上的 DTrace 工具。预计将来 DTrace 将会移植到 FreeBSD 和 Mac OS X 上。理论上也可以通过更改 src/include/pg_trace.h 中的 PG_TRACE 宏来支持其它跟踪工具。
跟踪点是默认禁止的,你必须在运行 configure 脚本时明确使用 --enable-dtrace 选项来启用 DTrace 支持。参见节14.5获取更多信息。
表25-3显示的是在源代码中提供的标准跟踪点,此外,也可以根据特定的具体问题添加其它跟踪点。
表25-3. 内置跟踪点
名字 | 参数 | 概述 |
---|---|---|
transaction__start | (int transactionId) | 开始新事务 |
transaction__commit | (int transactionId) | 事务成功完成 |
transaction__abort | (int transactionId) | 事务失败 |
lwlock__acquire | (int lockid, int mode) | 成功获取 LWLock |
lwlock__release | (int lockid, int mode) | 成功释放 LWLock |
lwlock__startwait | (int lockid, int mode) | 未立即获得 LWLock 并且后端开始等待该锁 |
lwlock__endwait | (int lockid, int mode) | 后端已释放等待的 LWLock |
lwlock__condacquire | (int lockid, int mode) | 成功获得调用者指定不必等待的 LWLock |
lwlock__condacquire__fail | (int lockid, int mode) | 未能获得调用者指定不必等待的 LWLock |
lock__startwait | (int locktag_field2, int lockmode) | 由于不能立即获取超重锁(lmgr lock)而进入等待 |
lock__endwait | (int locktag_field2, int lockmode) | 等待获取超重锁(lmgr lock)完毕(也就是成功获取) |
下面的例子示范了一个分析事务次数的 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 脚本时,跟踪点名字中的双下划线是如何被连字符取代的。执行完毕后,示范 D 脚本的输出如下:
# ./txn_count.d `pgrep -n postgres` ^C Start 71 Commit 70 Total time (ns) 2312105013
必须在实际使用跟踪程序前进行仔细的编写和充分的调试,否则收集到的跟踪信息可能毫无意义。大多数问题是由于外部跟踪程序错误导致的而不是底层系统。在讨论使用动态跟踪发现的信息时,应确保在其中包含你使用的跟踪脚本。
开发者可以在代码中任意位置定义新的跟踪点,当然这要重新编译之后才能生效。
可以使用一个跟踪宏来插入跟踪点。可以根据将在该跟踪点上检查多少变量来选择宏。使用跟踪点的名字就可以仅在一行代码上跟踪某个事件的发生,例如:
PG_TRACE (my__new__trace__point);
通过使用与跟踪点名字后面的参数个数一样多的 PG_TRACEn 宏,使用动态跟踪工具检查一个或多个变量,就可以提供更复杂的跟踪点:
PG_TRACE3 (my__complex__event, varX, varY, varZ);
transaction__start 跟踪点的定义如下:
static void StartTransaction(void) { ... /* * 生成一个新的事务 id */ s->transactionId = GetNewTransactionId(false); XactLockTableInsert(s->transactionId); PG_TRACE1(transaction__start, s->transactionId); ... }
请注意动态跟踪工具是如何获得事务 ID 的。
动态跟踪工具可能需要定义更多的跟踪点。例如,DTrace 要求在 src/backend/utils/probes.d 文件中添加新的跟踪点,如下所示:
provider postgresql { ... probe transaction__start(int); ... };
必须注意要将"探头"参数的数据类型与 PG_TRACE 宏使用的数据类型相匹配。编译时并不对此进行检查。你可以通过重新编译来检查新增的跟踪点是否可用,然后以 root 运行新的二进制文件,执行类似下述 DTrace 命令:
dtrace -l -n transaction-start