一个数据库管理员常常会疑惑,“系统现在正在做什么?”这一章会讨论如何搞清楚这个问题。
一些工具可以用来监控数据库活动并且分析性能。这一章的大部分都致力于描述PostgreSQL的统计收集器,但是我们也不能忽视常规的 Unix 监控程序,如ps
、top
、iostat
和vmstat
。另外,一旦我们发现了一个性能差的查询,可能需要PostgreSQL的EXPAIN命令来进行进一步的调查。
一、标准 Unix 工具
在大部分 Unix 平台上,PostgreSQL会修改由ps
报告的命令标题,这样个体服务器进程可以被标识。一个显示样例是
$ ps auxww | grep ^postgres
postgres 15551 0.0 0.1 57536 7132 pts/0 S 18:02 0:00 postgres -i
postgres 15554 0.0 0.0 57536 1184 ? Ss 18:02 0:00 postgres: background writer
postgres 15555 0.0 0.0 57536 916 ? Ss 18:02 0:00 postgres: checkpointer
postgres 15556 0.0 0.0 57536 916 ? Ss 18:02 0:00 postgres: walwriter
postgres 15557 0.0 0.0 58504 2244 ? Ss 18:02 0:00 postgres: autovacuum launcher
postgres 15558 0.0 0.0 17512 1068 ? Ss 18:02 0:00 postgres: stats collector
postgres 15582 0.0 0.0 58772 3080 ? Ss 18:04 0:00 postgres: joe runbug 127.0.0.1 idle
postgres 15606 0.0 0.0 58772 3052 ? Ss 18:07 0:00 postgres: tgl regression [local] SELECT waiting
postgres 15610 0.0 0.0 58772 3056 ? Ss 18:07 0:00 postgres: tgl regression [local] idle in transaction
(ps
的调用方式随不同的平台而变,但是显示的细节都差不多。这个例子来自于一个最近的 Linux 系统)。列在这里的第一个进程是主服务器进程。为它显示的命令参数是当它被启动时使用的那些。接下来的五个进程是由主进程自动启动的后台工作者进程(如果你已经设置系统为不启动统计收集器,“统计收集器”进程将不会出现;同样“自动清理发动”进程也可以被禁用)。剩余的每一个进程都是一个处理一个客户端连接的服务器进程。每个这种进程都会把它的命令行显示设置为这种形式
postgres: user database host activity
在该客户端连接的生命期中,用户、数据库以及(客户端)主机项保持不变,但是活动指示器会改变。活动可以是闲置
(即等待一个客户端命令)、在事务中闲置
(在一个BEGIN
块里等待客户端)或者一个命令类型名,例如SELECT
。还有,如果服务器进程正在等待一个其它会话持有的锁, 等待中
会被追加到上述信息中。在上面的例子中,我们可以推断:进程 15606 正在等待进程 15610 完成其事务并且因此释放一些锁(进程 15610 必定是阻塞者,因为没有其他活动会话。在更复杂的情况中,可能需要查看pg_locks系统视图来决定谁阻塞了谁)。
如果配置了cluster_name,则集簇的名字 也将会显示在ps
的输出中:
$ psql -c 'SHOW cluster_name'
cluster_name
--------------
server1
(1 row)
$ ps aux|grep server1
postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: server1: background writer
...
如果你已经关闭了update_process_title,那么活动指示器将不会被更新,进程标题仅在新进程被启动的时候设置一次。 在某些平台上这样做可以为每个命令节省可观的开销,但在其它平台上却不明显。
提示:
Solaris需要特别的处理。你必需使用
/usr/ucb/ps
而不是/bin/ps
。 你还必需使用两个w
标志,而不是一个。另外,你对postgres
命令的最初调用必须用一个比服务器进程提供的短的ps
状态显示。如果你没有满足全部三个要求,每个服务器进程的ps
输出将是原始的postgres
命令行。 command line.
二、统计收集器
PostgreSQL的统计收集器是一个支持收集和报告服务器活动信息的子系统。 目前,这个收集器可以对表和索引的访问计数,计数可以按磁盘块和个体行来进行。它还跟踪每个表中的总行数、每个表的清理和分析动作的信息。它也统计调用用户定义函数的次数以及在每次调用中花费的总时间。
PostgreSQL也支持报告有关系统正在干什么的 动态信息,例如当前正在被其他服务器进程执行的命令以及系统中存在哪些其他连接。 这个功能是独立于收集器进程存在的。
2.1. 统计收集配置
因为统计收集给查询执行增加了一些负荷,系统可以被配置为收集或不收集信息。这由配置参数控制,它们通常在postgresql.conf
中设置。
参数track_activities允许监控当前被任意服务器进程执行的命令。
参数track_counts控制是否收集关于表和索引访问的统计信息。
参数track_functions启用对用户定义函数使用的跟踪。
参数track_to_timing启用对块读写次数的监控。
通常这些参数被设置在postgresql.conf
中,这样它们会应用于所有服务器进程,但是可以在单个会话中使用SET命令打开或关闭它们(为了阻止普通用户对管理员隐藏他们的活动,只有超级用户被允许使用SET
来改变这些参数)。
统计收集器通过临时文件将收集到的信息传送给其他PostgreSQL进程。这些文件被存储在名字由stats_temp_directory参数指定的目录中,默认是pg_stat_tmp
。为了得到更好的性能,stats_temp_directory
可以被指向一个基于 RAM 的文件系统来降低物理 I/O 需求。当服务器被干净地关闭时,一份统计数据的永久拷贝被存储在pg_stat
子目录中,这样在服务器重启后统计信息能被保持。当在服务器启动时执行恢复时(例如立即关闭、服务器崩溃以及时间点恢复之后),所有统计计数器会被重置。
2.2. 查看统计信息
表2.2.1中列出了一些预定义视图 可以用来显示系统的当前状态。 表2.2.2中列出了另一些视图可以 显示统计收集的结果。你也可以使用底层统计函数(在 第2.3节中讨论)来建立自定义的视图。
在使用统计信息监控收集到的数据时,你必须了解这些信息并非是实时更新的。每个独立的服务器进程只在进入闲置状态之前才向收集器传送新的统计计数;因此正在进行的查询或事务并不影响显示出来的总数。同样,收集器本身也最多每PGSTAT_STAT_INTERVAL
毫秒(缺省为 500ms,除非在编译服务器的时候修改过)发送一 次新的报告。因此显示的信息总是落后于实际活动。但是由track_activities
收集的当前查询信息总是最新的。
另一个重点是当一个服务器进程被要求显示任何这些统计信息时,它首先取得收集器进程最近发出的报告并且接着为所有统计视图和函数使用这个快照,直到它的当前事务的结尾。因此只要你继续当前事务,统计数据将会一直显示静态信息。相似地,当任何关于所有会话的当前查询的信息在一个事务中第一次被请求时,这样的信息将被收集。并且在整个事务期间将显示相同的信息。这是一种特性而非缺陷,因为它允许你在该统计信息上执行多个查询并且关联结果而不用担心那些数字会在你不知情的情况下改变。但是如果你希望用每个查询都看到新结果,要确保在任何事务块之外做那些查询。或者,你可以调用pg_stat_clear_snapshot
(),那将丢弃当前事务的统计快照(如果有)。下一次对统计性信息的使用将导致获取一个新的快照。
一个事务也可以在视图pg_stat_xact_all_tables
、pg_stat_xact_sys_tables
、pg_stat_xact_user_tables
和pg_stat_xact_user_functions
中看到它自己的统计信息(还没有被传送给收集器)。这些数字并不像上面所述的那样行动,相反它们在事务期间持续被更新。
表 2.2.1. 动态统计视图
表 2.2.2 已收集统计信息的视图
视图名称 | 描述 |
---|---|
pg_stat_archiver | 只有一行,显示有关 WAL 归档进程活动的统计信息。详见pg_stat_archiver。 |
pg_stat_bgwriter | 只有一行,显示有关后台写进程的活动的统计信息。详见pg_stat_bgwriter。 |
pg_stat_database | 每个数据库一行,显示数据库范围的统计信息。详见pg_stat_database。 |
pg_stat_database_conflicts | 每个数据库一行,显示数据库范围的统计信息, 这些信息的内容是关于由于与后备服务器的恢复过程 发生冲突而被取消的查询。详见 pg_stat_database_conflicts。 |
pg_stat_all_tables | 当前数据库中每个表一行,显示有关访问指定表的统计信息。详见pg_stat_all_tables。 |
pg_stat_sys_tables | 和pg_stat_all_tables 一样,但只显示系统表。 |
pg_stat_user_tables | 和pg_stat_all_tables 一样,但只显示用户表。 |
pg_stat_xact_all_tables | 和pg_stat_all_tables 相似,但计数动作只在当前事务内发生(还没有被包括在pg_stat_all_tables 和相关视图中)。用于生存和死亡行数量的列以及清理和分析动作在此视图中不出现。 |
pg_stat_xact_sys_tables | 和pg_stat_xact_all_tables 一样,但只显示系统表。 |
pg_stat_xact_user_tables | 和pg_stat_xact_all_tables 一样,但只显示用户表。 |
pg_stat_all_indexes | 当前数据库中的每个索引一行,显示:表OID、索引OID、模式名、表名、索引名、 使用了该索引的索引扫描总数、索引扫描返回的索引记录数、使用该索引的简 单索引扫描抓取的活表(livetable)中数据行数。 当前数据库中的每个索引一行,显示与访问指定索引有关的统计信息。详见pg_stat_all_indexes。 |
pg_stat_sys_indexes | 和pg_stat_all_indexes 一样,但只显示系统表上的索引。 |
pg_stat_user_indexes | 和pg_stat_all_indexes 一样,但只显示用户表上的索引。 |
pg_statio_all_tables | 当前数据库中每个表一行(包括TOAST表),显示:表OID、模式名、表名、 从该表中读取的磁盘块总数、缓冲区命中次数、该表上所有索引的磁盘块读取总数、 该表上所有索引的缓冲区命中总数、在该表的辅助TOAST表(如果存在)上的磁盘块读取总数、 在该表的辅助TOAST表(如果存在)上的缓冲区命中总数、TOAST表的索引的磁盘块读 取总数、TOAST表的索引的缓冲区命中总数。 当前数据库中的每个表一行,显示有关在指定表上 I/O 的统计信息。详见pg_statio_all_tables。 |
pg_statio_sys_tables | 和pg_statio_all_tables 一样,但只显示系统表。 |
pg_statio_user_tables | 和pg_statio_all_tables 一样,但只显示用户表。 |
pg_statio_all_indexes | 当前数据库中每个索引一行,显示:表OID、索引OID、模式名、 表名、索引名、该索引的磁盘块读取总数、该索引的缓冲区命中总数。 当前数据库中的每个索引一行,显示与指定索引上的 I/O 有关的统计信息。详见pg_statio_all_indexespg_statio_all_indexespg_statio_all_indexes。 |
pg_statio_sys_indexes | 和pg_statio_all_indexes 一样,但只显示系统表上的索引。 |
pg_statio_user_indexes | 和pg_statio_all_indexes 一样,但只显示用户表上的索引。 |
pg_statio_all_sequences | 当前数据库中每个序列对象一行,显示:序列OID、模式名、序列名、序列的磁盘读取总数、序列的缓冲区命中总数。 当前数据库中的每个序列一行,显示与指定序列上的 I/O 有关的统计信息。详见pg_statio_all_sequences。 |
pg_statio_sys_sequences | 和pg_statio_all_sequences 一样,但只显示系统序列(目前没有定义系统序列,因此这个视图总是为空)。 |
pg_statio_user_sequences | 和pg_statio_all_sequences 一样,但只显示用户序列。 |
pg_stat_user_functions | 对于所有跟踪功能,函数的OID,模式,名称,数量 通话总时间,和自我的时间。自我时间是 在函数本身所花费的时间量,总时间包括 它调用函数所花费的时间。时间值以毫秒为单位。 每一个被跟踪的函数一行,显示与执行该函数有关的统计信息。详见pg_stat_user_functions。 |
pg_stat_xact_user_functions | 和pg_stat_user_functions 相似,但是只统计在当前事务期间的调用(还没有被包括在pg_stat_user_functions 中)。 |
针对每个索引的统计信息对于判断哪个索引正被使用以及它们的效果特别有用。
pg_statio_
系列视图主要用于判断缓冲区的效果。当实际磁盘读取数远小于缓冲区命中时,这个缓冲能满足大部分读请求而无需进行内核调用。但是,这些统计信息并没有给出所有的事情:由于PostgreSQL处理磁盘 I/O 的方式,不在PostgreSQL缓冲区中的数据库仍然驻留在内核的 I/O 缓存中,并且因此可以被再次读取而不需要物理磁盘读取。我们建议希望了解PostgreSQL I/O 行为更多细节的用户将PostgreSQL统计收集器和操作系统中允许观察内核处理 I/O 的工具一起使用。
表 2.2.3. pg_stat_activity
视图
列 | 类型 | 描述 |
---|---|---|
datid | oid | 这个后端连接到的数据库的OID |
datname | name | 这个后端连接到的数据库的名称 |
pid | integer | 这个后端的进程 ID |
usesysid | oid | 登录到这个后端的用户的 OID |
usename | name | 登录到这个后端的用户的名称 |
application_name | text | 连接到这个后端的应用的名称 |
client_addr | inet | 连接到这个后端的客户端的 IP 地址。如果这个域为空,它表示客户端通过服务器机器上的一个 Unix 套接字连接或者这是一个内部进程(如自动清理)。 |
client_hostname | text | 已连接的客户端的主机名,由client_addr 的反向 DNS 查找报告。这个域将只对 IP 连接非空,并且只有log_hostname被启用时才会非空。 |
client_port | integer | 客户端用以和这个后端通信的 TCP 端口号,如果使用 Unix 套接字则为-1 |
backend_start | timestamp with time zone | 这个进程被启动的时间。对客户端后端来说就是客户端连接到服务器的时间。 |
xact_start | timestamp with time zone | 这个进程的当前事务被启动的时间,如果没有活动事务则为空。如果当前查询是它的第一个事务,这一列等于query_start 。 |
query_start | timestamp with time zone | 当前活动查询被开始的时间,如果state 不是active ,这个域为上一个查询被开始的时间 |
state_change | timestamp with time zone | state 上一次被改变的时间 |
wait_event_type | text | 后端正在等待的事件类型,如果不存在则为 NULL。可能的值有:
|
wait_event | text | 如果后端当前正在等待,则是等待事件的名称,否则为 NULL。详见表2.2.4。 |
state | text | 这个后端的当前总体状态。可能的值是:
|
backend_xid | xid | 这个后端的顶层事务标识符(如果存在)。 |
backend_xmin | xid | 当前后端的xmin 范围。 |
query | text | 这个后端最近查询的文本。如果state 为active ,这个域显示当前正在执行的查询。在所有其他状态下,它显示上一个被执行的查询。默认情况下,查询文本会被截断至1024个字符,这个值可以通过参数track_activity_query_size更改。 |
backend_type | text | 当前后端的类型。可能的类型是 autovacuum launcher , autovacuum worker , logical replication launcher , logical replication worker , parallel worker , background writer , client backend , checkpointer , startup , walreceiver , walsender 以及 walwriter 。 除此以外,由扩展注册的后台Worker可能有额外的类型。 |
pg_stat_activity
视图将为每一个服务器进程有一行,显示与该进程的当前活动相关的信息。
注意:
wait_event
和state
列是独立的。如果一个后端处于active
状态,它可能是也可能不是某个事件上的waiting
。如果状态是active
并且wait_event
为非空,它意味着一个查询正在被执行,但是它被阻塞在系统中某处。
表 2.2.4. wait_event
描述
等待事件类型 | 等待事件名称 | 描述 |
---|---|---|
LWLock | ShmemIndexLock | 正等待在共享内存中查找或者分配空间。 |
OidGenLock | 正等待分配或者赋予一个 OID。 | |
XidGenLock | 正等待分配或者赋予一个事务 ID。 | |
ProcArrayLock | 正等待在事务结尾得到一个快照或者清除事务 ID。 | |
SInvalReadLock | 正等待从共享无效消息队列中检索或者移除消息。 | |
SInvalWriteLock | 正等待在共享无效消息队列中增加一个消息。 | |
WALBufMappingLock | 正等待在 WAL 缓冲区中替换一个页面。 | |
WALWriteLock | 正等待 WAL 缓冲区被写入到磁盘。 | |
ControlFileLock | 正等待读取或者更新控制文件或创建一个新的 WAL 文件。 | |
CheckpointLock | 正等待执行检查点。 | |
CLogControlLock | 正等待读取或者更新事务状态。 | |
SubtransControlLock | 正等待读取或者更新子事务信息。 | |
MultiXactGenLock | 正等待读取或者更新共享多事务状态。 | |
MultiXactOffsetControlLock | 正等待读取或者更新多事务偏移映射。 | |
MultiXactMemberControlLock | 正等待读取或者更新多事务成员映射。 | |
RelCacheInitLock | 正等待读取或者写入关系缓冲区初始化文件。 | |
CheckpointerCommLock | 正等待管理 fsync 请求。 | |
TwoPhaseStateLock | 正等待读取或者更新预备事务的状态。 | |
TablespaceCreateLock | 正等待创建或者删除表空间。 | |
BtreeVacuumLock | 正等待读取或者更新一个 B-树索引的 vacuum 相关的信息。 | |
AddinShmemInitLock | 正等待管理共享内存中的空间分配。 | |
AutovacuumLock | 自动清理工作者或者启动器正等待更新或者读取自动清理工作者的当前状态。 | |
AutovacuumScheduleLock | 正等待确认选中进行清理的表仍需要清理。 | |
SyncScanLock | 正等待为同步扫描得到一个表上扫描的开始位置。 | |
RelationMappingLock | 正等待更新用来存储目录到文件节点映射的关系映射文件。 | |
AsyncCtlLock | 正等待读取或者更新共享通知状态。 | |
AsyncQueueLock | 正等待读取或者更新通知消息。 | |
SerializableXactHashLock | 正等待检索或者存储有关可序列化事务的信息。 | |
SerializableFinishedListLock | 正等待访问已结束可序列化事务的列表。 | |
SerializablePredicateLockListLock | 正等待在由可序列化事务持有的所列表上执行一个操作。 | |
OldSerXidLock | 正等待读取或者记录冲突的可序列化事务。 | |
SyncRepLock | 正等待读取或者更新有关同步复制的信息。 | |
BackgroundWorkerLock | 正等待读取或者更新后台工作者状态。 | |
DynamicSharedMemoryControlLock | 正等待读取或者更新动态共享内存状态。 | |
AutoFileLock | 正等待更新postgresql.auto.conf 文件。 | |
ReplicationSlotAllocationLock | 正等待分配或者释放一个复制槽。 | |
ReplicationSlotControlLock | 正等待读取或者更新复制槽状态。 | |
CommitTsControlLock | 正等待读取或者更新事务提交时间戳。 | |
CommitTsLock | 正等待读取或者更新事务时间戳的最新设置值。 | |
ReplicationOriginLock | 正等待设置、删除或者使用复制源头。 | |
MultiXactTruncationLock | 正等待读取或者阶段多事务信息。 | |
OldSnapshotTimeMapLock | 正等待读取或者更新旧的快照控制信息。 | |
BackendRandomLock | 正等待产生一个随机数。 | |
LogicalRepWorkerLock | 正等待逻辑复制工作者上的动作完成。 | |
CLogTruncationLock | 正等待截断预写式日志或者等待预写式日志截断操作完成。 | |
clog | 正在等地clog (事务状态)缓冲区上的I/O。 | |
commit_timestamp | 正等待提交时间戳缓冲区上的 I/O。 | |
subtrans | 正等待子事务缓冲区上的 I/O。 | |
multixact_offset | 正等待多事务偏移缓冲区上的 I/O。 | |
multixact_member | 正等待多事务成员缓冲区上的 I/O。 | |
async | 正等待 async(通知)缓冲区上的 I/O。 | |
oldserxid | 正等待 oldserxid 缓冲区上的 I/O。 | |
wal_insert | 正等待把 WAL 插入到一个内存缓冲区。 | |
buffer_content | 正等待读取或者写入内存中的一个数据页。 | |
buffer_io | 正等待一个数据页面上的 I/O。 | |
replication_origin | 正等待读取或者更新复制进度。 | |
replication_slot_io | 正等待一个复制槽上的 I/O。 | |
proc | 正等待读取或者更新 fast-path 锁信息。 | |
buffer_mapping | 正等待把一个数据块与缓冲池中的一个缓冲区关联。 | |
lock_manager | 正等待增加或者检查用于后端的锁,或者正等待加入或者退出一个锁定组(并行查询使用)。 | |
predicate_lock_manager | 正等待增加或者检查谓词锁信息。 | |
parallel_query_dsa | 正等待并行查询动态共享内存分配锁。 | |
tbm | 正等待TBM共享迭代器锁。 | |
parallel_append | 在Parallel Append计划执行期间等待选择下一个子计划。 | |
parallel_hash_join | 在Parallel Hash计划执行期间等待分配或交换一块内存或者更新计数器。 | |
Lock | relation | 正等待获得一个关系上的锁。 |
extend | 正等待扩展一个关系。 | |
page | 正等待获得一个关系上的页面的锁。 | |
tuple | 正等待获得一个元组上的锁。 | |
transactionid | 正等待一个事务结束。 | |
virtualxid | 正等待获得一个虚拟 xid 锁。 | |
speculative token | 正等待获取一个 speculative insertion lock。 | |
object | 正等待获得一个非关系数据库对象上的锁。 | |
userlock | 正等待获得一个用户锁。 | |
advisory | 正等待获得一个咨询用户锁。 | |
BufferPin | BufferPin | 正等待在一个缓冲区上加 pin。 |
Activity | ArchiverMain | 正在归档进程的主循环中等待。 |
AutoVacuumMain | 正在autovacuum启动器进程的主循环中等待。 | |
BgWriterHibernate | 正在后台写入器进程中等待,休眠中。 | |
BgWriterMain | 正在后台写入器进程的后台工作者的主循环中等待。 | |
CheckpointerMain | 正在检查点进程的主循环中等待。 | |
LogicalApplyMain | 正在逻辑应用进程的主循环中等待。 | |
LogicalLauncherMain | 正在逻辑启动器进程的主循环中等待。 | |
PgStatMain | 正在统计收集器进程的主循环中等待。 | |
RecoveryWalAll | 在恢复时等待来自于任意类型来源(本地、归档或流)的WAL。 | |
RecoveryWalStream | 在恢复时等待来自于一个流的WAL。 | |
SysLoggerMain | 正在系统日志进程的主循环中等待。 | |
WalReceiverMain | 正在WAL接收器进程的主循环中等待。 | |
WalSenderMain | 正在WAL发送器进程的主循环中等待。 | |
WalWriterMain | 正在WAL写入器进程的主循环中等待。 | |
Client | ClientRead | 正等待从客户端读取数据。 |
ClientWrite | 正等待向客户端写入数据。 | |
LibPQWalReceiverConnect | 正在WAL接收器中等待建立与远程服务器的连接。 | |
LibPQWalReceiverReceive | 正在WAL接收器中等待从远程服务器接收数据。 | |
SSLOpenServer | 正在尝试连接期间等待SSL。 | |
WalReceiverWaitStart | 正等待startup进程发送流复制的初始数据。 | |
WalSenderWaitForWAL | 正在WAL发送器进程中等待WAL被刷写。 | |
WalSenderWriteData | 在WAL发送器进程中处理来自WAL接收器的回复时等待任意活动。 | |
Extension | Extension | 正在一个扩展中等待。 |
IPC | BgWorkerShutdown | 正等待后台工作者关闭。 |
BgWorkerStartup | 正等待后台工作者启动。 | |
BtreePage | 正等待继续并行B-树扫描所需的页号变得可用。 | |
ClogGroupUpdate | 正等待组领袖在事务结束时更新事务状态。 | |
ExecuteGather | 在执行Gather 节点时等待来自子进程的活动。 | |
Hash/Batch/Allocating | 正等待一个选出的Parallel Hash参与者分配哈希表。 | |
Hash/Batch/Electing | 正在选出一个Parallel Hash参与者来分配一个哈希表。 | |
Hash/Batch/Loading | 正等待其他Parallel Hash参与者完成装载哈希表。 | |
Hash/Build/Allocating | 正等待一个选出的Parallel Hash参与者分配初始哈希表。 | |
Hash/Build/Electing | 正在选出一个Parallel Hash参与者以分配初始哈希表。 | |
Hash/Build/HashingInner | 正等待其他Parallel Hash参与者完成对内关系的哈希操作。 | |
Hash/Build/HashingOuter | 正等待其他Parallel Hash参与者完成对外关系的哈希操作。 | |
Hash/GrowBatches/Allocating | 正等待一个选出的Parallel Hash参与者分配更多批次。 | |
Hash/GrowBatches/Deciding | 正在选出一个Parallel Hash参与者决定未来的批次增长。 | |
Hash/GrowBatches/Electing | 正在选出一个Parallel Hash参与者分配更多批次。 | |
Hash/GrowBatches/Finishing | 正在等待一个选出的Parallel Hash参与者决定未来的批次增长。 | |
Hash/GrowBatches/Repartitioning | 正等待其他Parallel Hash参与者完成重新分区。 | |
Hash/GrowBuckets/Allocating | 正等待一个选出的Parallel Hash参与者完成更多桶的分配。 | |
Hash/GrowBuckets/Electing | 正在选出一个Parallel Hash参与者分配更多桶。 | |
Hash/GrowBuckets/Reinserting | 正等待其他Parallel Hash参与者完成将元组插入到新桶的操作。 | |
LogicalSyncData | 正等待逻辑复制的远程服务器发送用于初始表同步的数据。 | |
LogicalSyncStateChange | 正等待逻辑复制的远程服务器更改状态。 | |
MessageQueueInternal | 正等待其他进程被挂接到共享消息队列。 | |
MessageQueuePutMessage | 正等待把一个协议消息写到一个共享消息队列。 | |
MessageQueueReceive | 正等待从一个共享消息队列接收字节。 | |
MessageQueueSend | 正等待向一个共享消息队列中发送字节。 | |
ParallelBitmapScan | 正等待并行位图扫描被初始化。 | |
ParallelCreateIndexScan | 正等待并行CREATE INDEX 工作者完成堆扫描。 | |
ParallelFinish | 正等待并行工作者完成计算。 | |
ProcArrayGroupUpdate | 正等待组领袖在事务结束时清除事务ID。 | |
ReplicationOriginDrop | 正等待一个复制源头变得不活跃以便被删除。 | |
ReplicationSlotDrop | 正等待一个复制槽变得不活跃以便被删除。 | |
SafeSnapshot | 正等待一个用于READ ONLY DEFERRABLE 事务的快照。 | |
SyncRep | 正在同步复制期间等待来自远程服务器的确认。 | |
Timeout | BaseBackupThrottle | 当有限流活动时在基础备份期间等待。 |
PgSleep | 正在调用pg_sleep 的进程中等待。 | |
RecoveryApplyDelay | 在恢复时等待应用WAL,因为它被延迟了。 | |
IO | BufFileRead | 正等待从一个缓存的文件中读取。 |
BufFileWrite | 正等待向一个缓存的文件中写入。 | |
ControlFileRead | 正等待从控制文件中读取。 | |
ControlFileSync | 正等待控制文件到达稳定存储。 | |
ControlFileSyncUpdate | 正等待对控制文件的更新到达稳定存储。 | |
ControlFileWrite | 正等待一个对控制文件的写入。 | |
ControlFileWriteUpdate | 正等待一个写操作更新控制文件。 | |
CopyFileRead | 正在文件拷贝操作期间等待一个读操作。 | |
CopyFileWrite | 正在文件拷贝操作期间等待一个写操作。 | |
DataFileExtend | 正等待一个关系数据文件被扩充。 | |
DataFileFlush | 正等待一个关系数据文件到达稳定存储。 | |
DataFileImmediateSync | 正等待一个关系数据文件的立即同步到达稳定存储。 | |
DataFilePrefetch | 正等待从一个关系数据文件中的一次异步预取。 | |
DataFileRead | 正等待一次对一个关系数据文件的读操作。 | |
DataFileSync | 正等待对一个关系数据文件的更改到达稳定存储。 | |
DataFileTruncate | 正等待一个关系数据文件被截断。 | |
DataFileWrite | 正等待一次对一个关系数据文件的写操作。 | |
DSMFillZeroWrite | 等待向一个动态共享内存备份文件中写零字节。 | |
LockFileAddToDataDirRead | 在向数据目录锁文件中增加一行时等待一个读操作。 | |
LockFileAddToDataDirSync | 在向数据目录锁文件中增加一行时等待数据到达稳定存储。 | |
LockFileAddToDataDirWrite | 在向数据目录锁文件中增加一行时等待一个写操作。 | |
LockFileCreateRead | 在创建数据目录锁文件期间等待读取。 | |
LockFileCreateSync | 在创建数据目录锁文件期间等待数据到达稳定存储。 | |
LockFileCreateWrite | 在创建数据目录锁文件期间等待一个写操作。 | |
LockFileReCheckDataDirRead | 在重新检查数据目录锁文件的过程中等待一个读操作。 | |
LogicalRewriteCheckpointSync | 在一个检查点期间等待逻辑重写映射到达稳定存储。 | |
LogicalRewriteMappingSync | 在一次逻辑重写期间等待映射数据到达稳定存储。 | |
LogicalRewriteMappingWrite | 在一次逻辑重写期间等待对映射数据的写操作。 | |
LogicalRewriteSync | 正等待逻辑重写映射到达稳定存储。 | |
LogicalRewriteWrite | 正等待对逻辑重写映射的写操作。 | |
RelationMapRead | 正等待对关系映射文件的读操作。 | |
RelationMapSync | 正等待关系映射文件到达稳定存储。 | |
RelationMapWrite | 正等待对关系映射文件的写操作。 | |
ReorderBufferRead | 在重排序缓冲区管理期间等待一个读操作。 | |
ReorderBufferWrite | 在重排序缓冲区管理期间等待一个写操作。 | |
ReorderLogicalMappingRead | 在重排序缓冲区管理期间等待对一个逻辑映射的读操作。 | |
ReplicationSlotRead | 正等待对一个复制槽控制文件的读操作。 | |
ReplicationSlotRestoreSync | 在把一个复制槽控制文件恢复到内存的过程中等待它到达稳定存储。 | |
ReplicationSlotSync | 正等待一个复制槽控制文件到达稳定存储。 | |
ReplicationSlotWrite | 正等待对一个复制槽控制文件的写操作。 | |
SLRUFlushSync | 在检查点或者数据库关闭期间等待SLRU数据到达稳定存储。 | |
SLRURead | 正等待对一个SLRU页面的读操作。 | |
SLRUSync | 正等待SLRU数据在一个页面写之后到达稳定存储。 | |
SLRUWrite | 正等待一个SLRU页面上的写操作。 | |
SnapbuildRead | 正等待一个序列化历史目录快照的读操作。 | |
SnapbuildSync | 正等待一个序列化历史目录快照到达稳定存储。 | |
SnapbuildWrite | 正等待一个序列化历史目录快照的写操作。 | |
TimelineHistoryFileSync | 正等待一个通过流复制接收到的时间线历史文件到达稳定存储。 | |
TimelineHistoryFileWrite | 正等待一个通过流复制接收到的时间线历史文件的读操作。 | |
TimelineHistoryRead | 正等待一个时间线历史文件上的读操作。 | |
TimelineHistorySync | 正等待一个新创建的时间线历史文件达到稳定存储。 | |
TimelineHistoryWrite | 正等待一个新创建的时间线历史文件上的写操作。 | |
TwophaseFileRead | 正等待一个两阶段状态文件的读操作。 | |
TwophaseFileSync | 正等待一个两阶段状态文件到达稳定存储。 | |
TwophaseFileWrite | 正等待一个两阶段状态文件的写操作。 | |
WALBootstrapSync | 在自举期间等待WAL到达稳定存储。 | |
WALBootstrapWrite | 在自举期间等待一个WAL页面的写操作。 | |
WALCopyRead | 在通过拷贝一个已有WAL段创建一个新的WAL段时等待一个读操作。 | |
WALCopySync | 正等待一个通过拷贝已有WAL段创建的新WAL段到达稳定存储。 | |
WALCopyWrite | 在通过拷贝一个已有WAL段创建一个新的WAL段时等待一个写操作。 | |
WALInitSync | 正等待一个新初始化的WAL文件到达稳定存储。 | |
WALInitWrite | 在初始化一个新的WAL文件期间等待一个写操作。 | |
WALRead | 正等待一次对一个WAL文件的读操作。 | |
WALSenderTimelineHistoryRead | 在walsender的时间线命令期间等待对一个时间线历史文件的读操作。 | |
WALSyncMethodAssign | 在指派WAL同步方法时等待数据到达稳定存储。 | |
WALWrite | 正等待一次对一个WAL文件的写操作。 |
注意:
对于扩展安装的切片(tranche),这个名称由扩展指定并且会被wait_event
显示出来。很有可能在其他后端不知道的情况下,用户在其中一个后端中注册了切片(通过在动态共享内存中分配),那么我们对这种情况会显示extension
。
下面的例子展示了如何查看等待事件
SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event is NOT NULL;
pid | wait_event_type | wait_event
------+-----------------+---------------
2540 | Lock | relation
6644 | LWLock | ProcArrayLock
(2 rows)
表 2.2.5. pg_stat_replication
视图
列 | 类型 | 描述 |
---|---|---|
pid | integer | 一个 WAL 发送进程的进程 ID |
usesysid | oid | 登录到这个 WAL 发送进程的用户的 OID |
usename | name | 登录到这个 WAL 发送进程的用户的名称 |
application_name | text | 连接到这个 WAL 发送进程的应用的名称 |
client_addr | inet | 连接到这个 WAL 发送进程的客户端的 IP 地址。 如果这个域为空,它表示该客户端通过服务器机器上的一个 Unix 套接字连接。 |
client_hostname | text | 连接上的客户端的主机名,由一次对client_addr 的逆向 DNS 查找报告。这个域将只对 IP 连接非空,并且只有在 log_hostname被启用时非空 |
client_port | integer | 客户端用来与这个 WAL 发送进程通讯的 TCP 端口号, 如果使用 Unix 套接字则为-1 |
backend_start | timestamp with time zone | 这个进程开始的时间,即客户端是何时连接到这个 WAL 发送进程的 |
backend_xmin | xid | 由hot_standby_feedback报告 的这个后备机的xmin 水平线。 |
state | text | 当前的 WAL 发送进程状态。 可能的值是:
|
sent_lsn | pg_lsn | 在这个连接上发送的最后一个预写式日志的位置 |
write_lsn | pg_lsn | 被这个后备服务器写入到磁盘的最后一个预写式日志的位置 |
flush_lsn | pg_lsn | 被这个后备服务器刷入到磁盘的最后一个预写式日志的位置 |
replay_lsn | pg_lsn | 被重放到这个后备服务器上的数据库中的最后一个预写式日志的位置 |
write_lag | interval | 在本地刷写近期的WAL与接收到后备服务器已经写入它(但还没有刷写或者应用)的通知之间流逝的时间。如果这台服务器被配置为一个同步后备,这可以用来计量在提交时synchronous_commit 的级别remote_write 所导致的延迟。 |
flush_lag | interval | 在本地刷写近期的WAL与接收到后备服务器已经写入并且刷写它(但还没有应用)的通知之间流逝的时间。如果这台服务器被配置为一个同步后备,这可以用来计量在提交时synchronous_commit 的级别on 所导致的延迟。 |
replay_lag | interval | 在本地刷写近期的WAL与接收到后备服务器已经写入它、刷写它并且应用它的通知之间流逝的时间。如果这台服务器被配置为一个同步后备,这可以用来计量在提交时synchronous_commit 的级别remote_apply 所导致的延迟。 |
sync_priority | integer | 在基于优先的同步复制中,这台后备服务器被选为同步后备的优先级。在基于规定数量的同步复制中,这个值没有效果。 |
sync_state | text | 这一台后备服务器的同步状态。 可能的值是:
|
pg_stat_replication
视图中将为每一个 WAL 发送进程包含一行,用来显示与该发送进程连接的后备服务器的复制统计信息。 这个视图中只会列出直接连接的后备机,下游后备服务器的信息不包含在此。
pg_stat_replication
视图中报告的滞后时间近期的WAL被写入、刷写并且重放以及发送器知道这一切所花的时间的度量。如果远程服务器被配置为一台同步后备,这些时间表示由每一种同步提交级别所带来(或者是可能带来)的提交延迟。对于一台异步后备,replay_lag
列是最近的事务变得对查询可见的延迟时间的近似值。如果后备服务器已经完全追上了发送服务器并且没有WAL活动,在短时间内将继续显示最近测到的滞后时间,再然后就会显示为NULL。
对于物理复制会自动测量滞后时间。逻辑解码插件可能会选择性地发出跟踪消息,如果它们没有这样做,跟踪机制将把滞后显示为NULL。
注意:
报告的滞后时间并非按照当前的重放速率该后备还有多久才能追上发送服务器的预测。在新的WAL被生成期间,这样一种系统将显示类似的时间,但是当发送器变为闲置时会显示不同的值。特别是当后备服务器完全追上时,
pg_stat_replication
显示的是写入、刷写及重放最近报告的WAL位置所花的时间而不是一些用户可能预期的零。这种做法与为近期的写事务测量同步提交和事务可见性延迟的目的一致。为了降低用户预期一种不同的滞后模型带来的混淆,在一个完全重放完的闲置系统上,lag列会在一段比较短的时间后回复成NULL。监控系统应该选择将这种情况表示为缺失数据、零或者继续显示最近的已知值。
表 2.2.6. pg_stat_wal_receiver
视图
列 | 类型 | 描述 |
---|---|---|
pid | integer | WAL 接收器进程的进程 ID |
status | text | WAL 接收器进程的活动状态 |
receive_start_lsn | pg_lsn | WAL 接收器启动时使用的第一个预写式日志位置 |
receive_start_tli | integer | WAL 接收器启动时使用的第一个时间线编号 |
received_lsn | pg_lsn | 已经接收到并且已经被刷入磁盘的最后一个预写式日志的位置,这个域的初始值是 WAL 接收器启动时使用的第一个日志位置 |
received_tli | integer | 已经接收到并且已经被刷入磁盘的最后一个预写式日志的时间线编号,这个域的初始值是 WAL 接收器启动时使用的第一个日志所在的时间线编号 |
last_msg_send_time | timestamp with time zone | 从源头 WAL 发送器接收到的最后一个消息的发送时间 |
last_msg_receipt_time | timestamp with time zone | 从源头 WAL 发送器接收到的最后一个消息的接收时间 |
latest_end_lsn | pg_lsn | 报告给源头 WAL 发送器的最后一个预写式日志位置 |
latest_end_time | timestamp with time zone | 报告给源头 WAL 发送器最后一个事务日志位置的时间 |
slot_name | text | 这个 WAL 接收器使用的复制槽的名称 |
sender_host | text | 这个WAL接收器连接到的PostgreSQL实例的主机。这可以是一个主机名、一个IP地址,如果连接是通过Unix套接字则是一个目录路径(为目录的情况可以被辨别出来,因为路径将总是一个绝对路径并且以/ 开头)。 |
sender_port | integer | 这个WAL接收器连接到的PostgreSQL实例的端口号。 |
conninfo | text | 这个 WAL 接收器使用的连接串,安全相关的域会被隐去。 |
pg_stat_wal_receiver
事务只包含一行,它显示了从 WAL 接收器所连接的服务器得到的有关该接收器的统计信息。
表 2.2.7. pg_stat_subscription
视图
列 | 类型 | 介绍 |
---|---|---|
subid | oid | 订阅的OID |
subname | text | 订阅的名称 |
pid | integer | 订阅工作者进程的进程ID |
relid | Oid | 工作者正在同步的关系的OID,对于主应用工作者为空 |
received_lsn | pg_lsn | 接收到的最后一个预写式日志位置,这个字段的初始值是0 |
last_msg_send_time | timestamp with time zone | 从源头WAL发送器接收到的最后一个消息的发送时间 |
last_msg_receipt_time | timestamp with time zone | 从源头WAL发送器接收到的最后一个消息的接收时间 |
latest_end_lsn | pg_lsn | 最后一个报告给源头WAL发送器的预写式日志位置 |
latest_end_time | timestamp with time zone | 报告给源头WAL发送器的最后一个预写式日志位置的时间 |
每一个订阅的主工作者都在pg_stat_subscription
视图中有一行(如果工作者没有运行则PID为空),处理被订阅表的初始数据拷贝操作的工作者还会有额外的行。
表 2.2.8. pg_stat_ssl
视图
列 | 类型 | 描述 |
---|---|---|
pid | integer | 一个后端或者 WAL 发送进程的进程 ID |
ssl | boolean | 如果在这个连接上使用了 SSL 则为真 |
version | text | 在用的 SSL 版本,如果这个连接上没有使用 SSL 则为 NULL |
cipher | text | 在用的 SSL 密码的名称,如果这个连接上没有使用 SSL 则为 NULL |
bits | integer | 使用的加密算法中的位数,如果这个连接上没有使用 SSL 则为 NULL |
compression | boolean | 如果使用了 SSL 压缩则为真,否则为假, 如果这个连接上没有使用 SSL 则为 NULL |
clientdn | text | 来自所使用的客户端证书的识别名(DN)域, 如果没有提供客户端证书或者这个连接上没有使用 SSL 则为 NULL。如果 DN 域长度超过 NAMEDATALEN (标准编译 中是 64 个字符),则它会被截断。 |
pg_stat_ssl
视图将为每一个后端或者 WAL 发送进程 包含一行,用来显示这个连接上的 SSL 使用情况。可以把它与 pg_stat_activity
或者 pg_stat_replication
通过 pid
列连接来得到更多有关该连接的细节。
表 2.2.9. pg_stat_archiver
视图
列 | 类型 | 描述 |
---|---|---|
archived_count | bigint | 已被成功归档的 WAL 文件数量 |
last_archived_wal | text | 最后一个被成功归档的 WAL 文件名称 |
last_archived_time | timestamp with time zone | 最后一次成功归档操作的时间 |
failed_count | bigint | 失败的归档 WAL 文件尝试的数量 |
last_failed_wal | text | 最后一次失败的归档操作的 WAL 文件名称 |
last_failed_time | timestamp with time zone | 最后一次失败的归档操作的时间 |
stats_reset | timestamp with time zone | 这些统计信息最后一次被重置的时间 |
The pg_stat_archiver
视图将总是一个单一的行, 该行包含着有关集簇的归档进程的数据。
表 2.2.10. pg_stat_bgwriter
视图
列 | 类型 | 描述 |
---|---|---|
checkpoints_timed | bigint | 已经被执行的计划中检查点的数量 |
checkpoints_req | bigint | 已经被执行的请求检查点的数量 |
checkpoint_write_time | double precision | 在文件被写入磁盘的检查点处理部分花费的总时间,以毫秒计 |
checkpoint_sync_time | double precision | 在文件被同步到磁盘中的检查点处理部分花费的总时间,以毫秒计 |
buffers_checkpoint | bigint | 在检查点期间被写的缓冲区数目 |
buffers_clean | bigint | 被后台写进程写的缓冲区数目 |
maxwritten_clean | bigint | 后台写进程由于已经写了太多缓冲区而停止清洁扫描的次数 |
buffers_backend | bigint | 被一个后端直接写的缓冲区数量 |
buffers_backend_fsync | bigint | 一个后端不得不自己执行fsync 调用的次数(通常即使后端自己进行写操作,后台写进程也会处理这些) |
buffers_alloc | bigint | 被分配的缓冲区数量 |
stats_reset | timestamp with time zone | 这些统计信息上次被重置的时间 |
pg_stat_bgwriter
视图将总是只有单独的一行,它包含集簇的全局数据。
表 2.2.11. pg_stat_database
视图
列 | 类型 | 描述 |
---|---|---|
datid | oid | 一个数据库的 OID |
datname | name | 这个数据库的名称 |
numbackends | integer | 当前连接到这个数据库的后端数量。这是在这个视图中唯一一个返回反映当前状态值的列。所有其他列返回从上次重置以来积累的值。 |
xact_commit | bigint | 在这个数据库中已经被提交的事务的数量 |
xact_rollback | bigint | 在这个数据库中已经被回滚的事务的数量 |
blks_read | bigint | 在这个数据库中被读取的磁盘块的数量 |
blks_hit | bigint | 磁盘块被发现已经在缓冲区中的次数,这样不需要一次读取(这只包括 PostgreSQL 缓冲区中的命中,而不包括在操作系统文件系统缓冲区中的命中) |
tup_returned | bigint | 在这个数据库中被查询返回的行数 |
tup_fetched | bigint | 在这个数据库中被查询取出的行数 |
tup_inserted | bigint | 在这个数据库中被查询插入的行数 |
tup_updated | bigint | 在这个数据库中被查询更新的行数 |
tup_deleted | bigint | 在这个数据库中被查询删除的行数 |
conflicts | bigint | 由于与恢复冲突而在这个数据库中被取消的查询的数目(冲突只发生在后备服务器上,详见pg_stat_database_conflicts)。 |
temp_files | bigint | 在这个数据库中被查询创建的临时文件的数量。所有临时文件都被统计,不管为什么创建这些临时文件(如排序或哈希),并且不管log_temp_files设置。 |
temp_bytes | bigint | 在这个数据库中被查询写到临时文件中的数据总量。所有临时文件都被统计,不管为什么创建这些临时文件(如排序或哈希),并且不管log_temp_fileslog_temp_fileslog_temp_files设置。 |
deadlocks | bigint | 在这个数据库中被检测到的死锁数 |
blk_read_time | double precision | 在这个数据库中后端花费在读取数据文件块的时间,以毫秒计 |
blk_write_time | double precision | 在这个数据库中后端花费在写数据文件块的时间,以毫秒计 |
stats_reset | timestamp with time zone | 这些统计信息上次被重置的时间 |
pg_stat_database
视图将为集簇中的每一个数据库包含有一行,每一行显示数据库范围的统计信息。
表 2.2.12. pg_stat_database_conflicts
视图
列 | 类型 | 描述 |
---|---|---|
datid | oid | 一个数据库的 OID |
datname | name | 这个数据库的名称 |
confl_tablespace | bigint | 这个数据库中由于表空间被删掉而取消的查询数量 |
confl_lock | bigint | 这个数据库中由于锁超时而取消的查询数量 |
confl_snapshot | bigint | 这个数据库中由于旧快照而取消的查询数量 |
confl_bufferpin | bigint | 这个数据库中由于被占用的缓冲区而取消的查询数量 |
confl_deadlock | bigint | 这个数据库中由于死锁而取消的查询数量 |
pg_stat_database_conflicts
视图为每一个 数据库包含一行,用来显示数据库范围内由于与后备服务器上的恢复过程 冲突而被取消的查询的统计信息。 这个视图将只包含后备服务器上的信息, 因为冲突会不发生在主服务器上。
表 2.2.13. pg_stat_all_tables
视图
列 | 类型 | 描述 |
---|---|---|
relid | oid | 一个表的 OID |
schemaname | name | 这个表所在的模式的名称 |
relname | name | 这个表的名称 |
seq_scan | bigint | 在这个表上发起的顺序扫描的次数 |
seq_tup_read | bigint | 被顺序扫描取得的活着的行的数量 |
idx_scan | bigint | 在这个表上发起的索引扫描的次数 |
idx_tup_fetch | bigint | 被索引扫描取得的活着的行的数量 |
n_tup_ins | bigint | 被插入的行数 |
n_tup_upd | bigint | 被更新的行数(包括 HOT 更新的行) |
n_tup_del | bigint | 被删除的行数 |
n_tup_hot_upd | bigint | 被更新的 HOT 行数(即不要求独立索引更新的行更新) |
n_live_tup | bigint | 活着的行的估计数量 |
n_dead_tup | bigint | 死亡行的估计数量 |
n_mod_since_analyze | bigint | 从这个表最后一次被分析后备修改的行的估计数量 |
last_vacuum | timestamp with time zone | 上次这个表被手动清理的时间(不统计VACUUM FULL ) |
last_autovacuum | timestamp with time zone | 上次这个表被自动清理守护进程清理的时间 |
last_analyze | timestamp with time zone | 上次这个表被手动分析的时间 |
last_autoanalyze | timestamp with time zone | 上次这个表被自动清理守护进程分析的时间 |
vacuum_count | bigint | 这个表已被手工清理的次数(不统计VACUUM FULL ) |
autovacuum_count | bigint | 这个表已被自动清理守护进程清理的次数 |
analyze_count | bigint | 这个表已被手工分析的次数 |
autoanalyze_count | bigint | 这个表已被自动清理守护进程分析的次数 |
pg_stat_all_tables
视图将为当前数据库中的每一个表(包括 TOAST 表)包含一行,该行显示与对该表的访问相关的统计信息。pg_stat_user_tables
和pg_stat_sys_tables
视图包含相同的信息,但是被过滤得分别只显示用户和系统表。
表 2.2.14. pg_stat_all_indexes
视图
列 | 类型 | 描述 |
---|---|---|
relid | oid | 这个索引的基表的 OID |
indexrelid | oid | 这个索引的 OID |
schemaname | name | 这个索引所在的模式的名称 |
relname | name | 这个索引的基表的名称 |
indexrelname | name | 这个索引的名称 |
idx_scan | bigint | 在这个索引上发起的索引扫描次数 |
idx_tup_read | bigint | 在这个索引上由扫描返回的索引项数量 |
idx_tup_fetch | bigint | 被使用这个索引的简单索引扫描取得的活着的表行数量 |
pg_stat_all_indexes
视图将为当前数据库中的每个索引包含一行,该行显示关于对该索引访问的统计信息。pg_stat_user_indexes
和pg_stat_sys_indexes
视图包含相同的信息,但是被过滤得只分别显示用户和系统索引。
索引可以被简单索引扫描、“位图”索引扫描以及优化器使用。在一次位图扫描中,多个索引的输出可以被通过 AND 或 OR 规则组合,因此当使用一次位图扫描时难以将取得的个体堆行与特定的索引关联起来。因此,一次位图扫描会增加它使用的索引的pg_stat_all_indexes
.idx_tup_read
计数,并且为每个表增加pg_stat_all_tables
.idx_tup_fetch
计数,但是它不影响pg_stat_all_indexes
.idx_tup_fetch
。如果所提供的常量值不在优化器统计信息记录的范围之内,优化器也会访问索引来检查,因为优化器统计信息可能已经“不新鲜”了。
注意:
即使不用位图扫描,
idx_tup_read
和idx_tup_fetch
计数也可能不同,因为idx_tup_read
统计从该索引取得的索引项而idx_tup_fetch
统计从表取得的或者的行。如果使用该索引取得了任何死亡行或还未提交的行,或者如果通过一次只用索引扫描的方式避免了任何堆获取,后者将较小。
表 2.2.15. pg_statio_all_tables
视图
列 | 类型 | 描述 |
---|---|---|
relid | oid | 一个表的 OID |
schemaname | name | 这个表所在的模式的名称 |
relname | name | 这个表的名称 |
heap_blks_read | bigint | 从这个表读取的磁盘块数量 |
heap_blks_hit | bigint | 在这个表中的缓冲区命中数量 |
idx_blks_read | bigint | 从这个表上所有索引中读取的磁盘块数 |
idx_blks_hit | bigint | 在这个表上的所有索引中的缓冲区命中数量 |
toast_blks_read | bigint | 从这个表的 TOAST 表(如果有)读取的磁盘块数 |
toast_blks_hit | bigint | 在这个表的 TOAST 表(如果有)中的缓冲区命中数量 |
tidx_blks_read | bigint | 从这个表的 TOAST 表索引(如果有)中读取的磁盘块数 |
tidx_blks_hit | bigint | 在这个表的 TOAST 表索引(如果有)中的缓冲区命中数量 |
pg_statio_all_tables
视图将为当前数据库中的每个表(包括 TOAST 表)包含一行,该行显示指定表上有关 I/O 的统计信息。pg_statio_user_tables
和pg_statio_sys_tables
视图包含相同的信息,但是被过滤得分别只显示用户表和系统表。
表 2.2.16. pg_statio_all_indexes
视图
列 | 类型 | 描述 |
---|---|---|
relid | oid | 这个索引的基表的 OID |
indexrelid | oid | 这个索引的 OID |
schemaname | name | 这个索引所在的模式的名称 |
relname | name | 这个索引的基表的名称 |
indexrelname | name | 这个索引的名称 |
idx_blks_read | bigint | 从这个索引读取的磁盘块数 |
idx_blks_hit | bigint | 在这个索引中的缓冲区命中数量 |
pg_statio_all_indexes
视图将为当前数据库中的每个索引包含一行,该行显示指定索引上有关 I/O 的统计信息。pg_statio_user_indexes
和pg_statio_sys_indexes
视图包含相同的信息,但是被过滤得分别只显示用户索引和系统索引。
表 2.2.17. pg_statio_all_sequences
视图
列 | 类型 | 描述 |
---|---|---|
relid | oid | 一个序列的 OID |
schemaname | name | 这个序列所在的模式的名称 |
relname | name | 这个序列的名称 |
blks_read | bigint | 从这个序列中读取的磁盘块数 |
blks_hit | bigint | 在这个序列中的缓冲区命中数量 |
pg_statio_all_sequences
视图将为当前数据库中的每个序列包含一行,该行显示在指定序列上有关 I/O 的统计信息。
表 2.2.18. pg_stat_user_functions
视图
列 | 类型 | 描述 |
---|---|---|
funcid | oid | 一个函数的 OID |
schemaname | name | 这个函数所在的模式的名称 |
funcname | name | 这个函数的名称 |
calls | bigint | 这个函数已经被调用的次数 |
total_time | double precision | 在这个函数以及它所调用的其他函数中花费的总时间,以毫秒计 |
self_time | double precision | 在这个函数本身花费的总时间,不包括被它调用的其他函数,以毫秒计 |
pg_stat_user_functions
视图将为每一个被追踪的函数包含一行,该行显示有关该函数执行的统计信息。track_functions参数控制到底哪些函数被跟踪。
28.2.3. 统计函数
其他查看统计信息的方法是直接使用查询,这些查询使用上述标准视图用到的底层统计信息访问函数。如要了解如函数名等细节,可参考标准视图的定义(例如,在psql中你可以发出\d+ pg_stat_activity
)。针对每一个数据库统计信息的访问函数把一个数据库 OID 作为参数来标识要报告哪个数据库。而针对每个表和每个索引的函数要求表或索引 OID。针对每个函数统计信息的函数用一个函数 OID。注意只有在当前数据库中的表、索引和函数才能被这些函数看到。
与统计收集相关的额外函数被列举在表2.2.19中。
表 2.2.19. 额外统计函数
pg_stat_get_activity
是pg_stat_activity
视图的底层函数,它返回一个行集合,其中包含有关每个后端进程所有可用的信息。有时只获得该信息的一个子集可能会更方便。在那些情况中,可以使用一组更老的针对每个后端的统计访问函数,这些显示在表2.2.20中。这些访问函数使用一个后端 ID 号,范围从 1 到当前活动后端数目。函数pg_stat_get_backend_idset
提供了一种方便的方法为每个活动后端产生一行来调用这些函数。例如,要显示PID以及所有后端当前的查询:
SELECT pg_stat_get_backend_pid(s.backendid) AS pid,
pg_stat_get_backend_activity(s.backendid) AS query
FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s;
表 2.2.20. 针对每个后端的统计函数
函数 | 返回类型 | 描述 |
---|---|---|
| setof integer | 当前活动后端 ID 号的集合(从 1 到活动后端数目) |
| text | 这个后端最近查询的文本 |
| timestamp with time zone | 最近查询被开始的时间 |
| inet | 该客户端连接到这个后端的 IP 地址 |
| integer | 该客户端用来通信的 TCP 端口号 |
| oid | 这个后端连接到的数据库的 OID |
| integer | 这个后端的进程 ID |
| timestamp with time zone | 这个进程被开始的时间 |
| oid | 登录到这个后端的用户的 OID |
| text | 如果后端正在等待,则是等待事件类型的名称,否则为 NULL。详见表2.2.4。 |
| text | 如果后端正在等待,则是等待事件的名称,否则为 NULL。详见表2.2.4。 |
| timestamp with time zone | 当前事务被开始的时间 |
三、查看锁
监控数据库活动的另外一个有用的工具是pg_locks
系统表。这样就允许数据库管理员查看在锁管理器里面未解决的锁的信息。例如,这个功能可以被用于:
-
查看当前所有未解决的锁、在一个特定数据库中的关系上所有的锁、在一个特定关系上所有的锁,或者由一个特定PostgreSQL会话持有的所有的锁。
-
判断当前数据库中带有最多未授予锁的关系(它很可能是数据库客户端的竞争源)。
-
判断锁竞争给数据库总体性能带来的影响,以及锁竞争随着整个数据库流量的变化范围。
四、进度报告
PostgreSQL有能力在命令执行期间报告特定命令的进度。当前,唯一一种支持进度报告的命令是VACUUM
。这在未来可能会被扩充。
4.1. VACUUM进度报告
只要VACUUM
正在运行,每一个当前正在清理的后端(包括autovacuum工作者进程)在pg_stat_progress_vacuum
视图中都会有一行。下面的表描述了将被报告的信息并且提供了如何解释它们的信息。进度报告当前不支持VACUUM FULL
,运行着VACUUM FULL
的后端将不会在这个视图中列出。
表 4.1.1. pg_stat_progress_vacuum
视图
列 | 类型 | 描述 |
---|---|---|
pid | integer | 后端的进程ID。 |
datid | oid | 这个后端连接的数据库的OID。 |
datname | name | 这个后端连接的数据库的名称。 |
relid | oid | 被vacuum的表的OID。 |
phase | text | vacuum的当前处理阶段。请参考表4.1.2。 |
heap_blks_total | bigint | 该表中堆块的总数。这个数字在扫描开始时报告,之后增加的块将不会(并且不需要)被这个VACUUM 访问。 |
heap_blks_scanned | bigint | 被扫描的堆块数量。由于可见性映射被用来优化扫描,一些块将被跳过而不做检查,被跳过的块会被包括在这个总数中,因此当清理完成时这个数字最终将会等于heap_blks_total 。仅当处于扫描堆 阶段时这个计数器才会前进。 |
heap_blks_vacuumed | bigint | 被清理的堆块数量。除非表没有索引,这个计数器仅在处于清理堆 阶段时才会前进。不包含死亡元组的块会被跳过,因此这个计数器可能有时会向前跳跃一个比较大的增量。 |
index_vacuum_count | bigint | 已完成的索引清理周期数。 |
max_dead_tuples | bigint | 在需要执行一个索引清理周期之前我们可以存储的死亡元组数,取决于maintenance_work_mem。 |
num_dead_tuples | bigint | 从上一个索引清理周期以来收集的死亡元组数。 |
表 4.1.2. VACUUM的阶段
阶段 | 描述 |
---|---|
初始化 | VACUUM 正在准备开始扫描堆。这个阶段应该很简短。 |
扫描堆 | VACUUM 正在扫描堆。如果需要,它将会对每个页面进行修建以及碎片整理,并且可能会执行冻结动作。heap_blks_scanned 列可以用来监控扫描的进度。 |
清理索引 | VACUUM 当前正在清理索引。如果一个表拥有索引,那么每次清理时这个阶段会在堆扫描完成后至少发生一次。如果maintenance_work_memmaintenance_work_memmaintenance_work_mem不足以存放找到的死亡元组,则每次清理时会多次清理索引。 |
清理堆 | VACUUM 当前正在清理堆。清理堆与扫描堆不是同一个概念,清理堆发生在每一次清理索引的实例之后。如果heap_blks_scanned 小于heap_blks_total ,系统将在这个阶段完成之后回去扫描堆;否则,系统将在这个阶段完成后开始清理索引。 |
清除索引 | VACUUM 当前正在清除索引。这个阶段发生在堆被完全扫描并且对堆和索引的所有清理都已经完成以后。 |
截断堆 | VACUUM 正在截断堆,以便把关系尾部的空页面返还给操作系统。这个阶段发生在清除完索引之后。 |
执行最后的清除 | VACUUM 在执行最终的清除。在这个阶段中,VACUUM 将清理空闲空间映射、更新pg_class 中的统计信息并且将统计信息报告给统计收集器。当这个阶段完成时,VACUUM 也就结束了。 |
五、动态追踪
PostgreSQL提供了功能来支持数据库服务器的动态追踪。这样就允许在代码中的特 定点上调用外部工具来追踪执行过程。
一些探针或追踪点已经被插入在源代码中。这些探针的目的是被数据库开发者和管理员使用。默认情况下,探针不被编译到PostgreSQL中;用户需要显式地告诉配置脚本使得探针可用。
目前,在写本文当时DTrace已被支持,它在 Solaris、macOS、FreeBSD、NetBSD 和 Oracle Linux 上可用。Linux 的SystemTap项目提供了一种可用的 DTrace 等价物。支持其他动态追踪工具在理论上可以通过改变src/include/utils/probes.h
中的宏定义实现。
5.1. 动态追踪的编译
默认情况下,探针是不可用的,因此你将需要显式地告诉配置脚本让探针在PostgreSQL中可用。要包括 DTrace 支持,在配置时指定--enable-dtrace
。
5.2. 内建探针
如表5.2.1所示,源代码中提供了一些标准探针。表5.2.2显式了在探针中使用的类型。当然,可以增加更多探针来增强PostgreSQL的可观测性。
表 5.2.1. 内建 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, int) | 当一次排序操作开始时触发的探针。arg0 指示是堆排序、索引排序或数据排序。arg1 为真表示唯一值强制。arg2 是键列的数目。arg3 是允许使用的工作内存数(以千字节计)。如果要求随机访问排序结果,那么 arg4 为真。arg5为0 时表示串行,为1 时表示并行工作者,为2 时表示并行领袖。 |
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 | () | 当死锁检测器发现死锁时触发的探针。 |
表 5.2.2. 定义用在探针参数中的类型
类型 | 定义 |
---|---|
LocalTransactionId | unsigned int |
LWLockMode | int |
LOCKMODE | int |
BlockNumber | unsigned int |
oid | unsigned int |
ForkNumber | int |
bool | char |
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 脚本需要细心地编写和调试,否则被收集的追踪信息可能会毫无意义。在大部分发现问题的情况中,它就是发生问题的部件,而不是底层系统。当讨论使用动态追踪发现的信息时,一定要封闭使用的脚本来允许这些以便被检查和讨论。
5.4. 定义新探针
开发者可以在代码中任意位置定义新的探针,当然这要重新编译之后才能生效。下面是插入新探针的步骤:
-
决定探针名称以及探针可用的数据
-
把该探针定义加入到
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
是文档名。
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
宏。