27.2. 统计收集器

PostgreSQL统计收集器是一个支持收集和汇报服务器活跃性信息的子系统。 目前,这个收集器可以给出对表和索引的访问计数,包括磁盘块的数量和独立行的项。 它还跟踪每个表中的行的总数,每个表的过去的真空和分析时间。它也可以计算用户定义的函数的调用,和在每个人的总花费的时间。

PostgreSQL还可以判断当前其它服务器进程正在执行的命令是什么。 这是一个收集过程中的独立设施。

27.2.1. 统计收集器配置

因为统计收集给查询处理增加了一些开销,所以你可以启用或禁用统计收集。这是由配置参数控制的, 通常在postgresql.conf里设置(参阅Chapter 18获取有关设置配置参数的细节)。

这个参数track_counts控制关于表和索引是否被统计。

参数track_functions实现了对用户子弟年故意函数用法的追踪。

参数track_activities控制是否监视每个服务器进程当前执行的命令字符串。

通常这些参数在postgresql.conf中设置,因此它们作用于所有服务器进程, 但是我们也可以在独立的会话里用SET命令把它们打开或者关闭。 为避免普通用户把它们的活跃性隐藏不给管理员看, 只有超级用户允许用SET命令修改这些参数。

统计收集器与后端通信通过临时文件获得需要的信息(包括autovacuum)。 这些文件存放在pg_stat_tmp子目录中。 当postmaster关闭时,一个统计数据的永久副本存放在global 子目录中。为了提高性能,stats_temp_directory参数 可以指向一个依据RAM的文件系统,降低物理I/O需求。

27.2.2. 查看收集到的统计信息

有一些预定义的视图可以用于显示统计收集的结果,在表Table 27-1里列出。另外,我们可以使用底层的统计函数制作自定义的视图。

在使用统计观察当前活跃性的时候,你必须意识到这些信息并不是实时更新的。 每个独立的服务器进程只是在准备进入空闲状态的时候才向收集器传送新的块和行访问计数; 因此正在处理的查询或者事务并不影响显示出来的总数。同样,收集器本身也最 多每PGSTAT_STAT_INTERVAL毫秒(缺省500,除非在编译服务器的时候修改过)发送一 次新的报告。因此显示总是落后于实际活动。但是由track_activities收集的当前查 询信息总是实时更新的。

另外一个需要着重指出的是,在请求服务器进程显示任何这些统计信息的时候, 它首先抓取收集器进程发出的最新报告,然后就拿这些数据作为所有统计视图 和函数的快照,直到它当前的事务结束。因此统计信息在当前事务的持续期间内不会改 变。类似的,每个进程的当前查询信息在该查询首次出现在事务中的时候就被收集了, 并且在整个事务过程中都显示相同的信息。这是一个特性,而不是一个臭虫,因为这样 就允许你在统计上执行几个查询并且对结果进行相关性检查而又不用担心这些数字会悄悄 的变化。但是如果你想看每个查询的最新结果,那么就要记住在事务块外面处理这些查询。 另外,你可以调用pg_stat_clear_snapshot,这将丢弃 目前交易的统计数据快照(如有)。下次使用 统计信息将导致获取一个新的快照

Table 27-1. 标准统计视图

视图名字描述
pg_stat_activity 每个服务器进程一行,显示:数据库OID、数据库名、进程ID、用户OID、用户名、客户的地址和端口号,服务器进程, 当前交易、当前查询等待状态、当前查询开始执行的时间、进程启动的时间、进程的等待状态和文字当前查询。。报告当前查询相关信息的各个字段只有在打开track_activities参数 的时候才可用。另外,这些列是唯一可见的,如果检查视图的用户是一个超级用户或、拥有相同进程的用户。
只有一行,通过后台写进程来显示集群范围内的统计数据: 预计检查点的数量,要求的检查点,检查点写入的缓冲区和清理扫描, 以及后台写进程停滞一个清理扫描的时间(因为需要写很多缓冲区)。 同时还包括共享缓冲池的统计数据,包括后台进程写入的缓冲区( 也就是说,不是通过后台写进程)以及分配的总缓冲区。 
pg_stat_database 每个数据库一行, 显示:数据库OID、数据库名、与该数据库连接的活跃服务器进程数、 已提交的事务总数、已回滚的事务总、已读取的磁盘块总数、缓冲区命中总数 (在缓冲区中找到所需要的块,从而避免读取块的动作)。返回插入,抓取,更新,删除的列数。
pg_stat_all_tables 当前数据库中每个表一行(包括TOAST表),显示:表OID、模式名、表名、发起的 顺序扫描总数、顺序扫描抓取的活数据行(liverow)的数目、发起的索引扫描的总数 (属于该表的所有索引)、索引扫描抓取的活数据行的数目、插入的行总数、更新的行总数 、删除的行总数、数行更新,它们是热(即,没有单独的索引更新), 活的和死行数,上次手动清理该表的时间、上次由autovacuum自动清理该表的时间、 上次手动分析该表的时间、上次由autovacuum自动分析该表的时间。
pg_stat_sys_tablespg_stat_all_tables一样,但只显示系统表。
pg_stat_user_tablespg_stat_all_tables一样,但只显示用户表。
pg_stat_all_indexes 当前数据库的每个索引一行,显示:表OID、索引OID、模式名、表名、索引名、 使用了该索引的索引扫描总数、索引扫描返回的索引记录数、使用该索引的简 单索引扫描抓取的活表(livetable)中数据行数。
pg_stat_sys_indexespg_stat_all_indexes一样,但只显示系统表上的索引。
pg_stat_user_indexespg_stat_all_indexes一样,但只显示用户表上的索引。
pg_statio_all_tables 当前数据库中每个表一行(包括TOAST表),显示:表OID、模式名、表名、 从该表中读取的磁盘块总数、缓冲区命中次数、该表上所有索引的磁盘块读取总数、 该表上所有索引的缓冲区命中总数、在该表的辅助TOAST表(如果存在)上的磁盘块读取总数、 在该表的辅助TOAST表(如果存在)上的缓冲区命中总数、TOAST表的索引的磁盘块读 取总数、TOAST表的索引的缓冲区命中总数。
pg_statio_sys_tablespg_statio_all_tables一样,但只显示系统表。
pg_statio_user_tablespg_statio_all_tables一样,但只显示用户表。
pg_statio_all_indexes 当前数据库中每个索引一行,显示:表OID、索引OID、模式名、 表名、索引名、该索引的磁盘块读取总数、该索引的缓冲区命中总数。
pg_statio_sys_indexes 和pg_statio_all_indexes一样,但只显示系统表。
pg_statio_user_indexes 和pg_statio_all_indexes一样,但只显示用户表。
pg_statio_all_sequences 当前数据库中每个序列对象一行,显示:序列OID、模式名、序列名、序列的磁盘读取总数、序列的缓冲区命中总数。
pg_statio_sys_sequencespg_statio_all_sequences一样,但只系统序列。因为目前没有定义系统序列,所以这个视图总是空的。
pg_statio_user_sequences 和pg_statio_all_sequences一样,但只显示用户序列。
pg_stat_user_functions 对于所有跟踪功能,函数的OID,模式,名称,数量 通话总时间,和自我的时间。自我时间是 在函数本身所花费的时间量,总时间包括 它调用函数所花费的时间。时间值以毫秒为单位。

针对每个索引的统计对于判断哪个索引得到使用以及它们的效果非常有用

PostgreSQL8.1开始,索引既可以直接使用,也可以通过"位图扫描"使用。 在位图扫描中,多个索引的输出可以通过AND或者OR规则合并;所以,在使用索引的时候, 很难把独立的堆(表)行抓取和指定的索引抓取结合起来。因此,位图扫描增大它使用 的pg_stat_all_indexes计数。并且它还增加为表使用的 idx_tup_fetch计数,但是它并不影 响pg_stat_all_indexes. idx_tup_fetch

Note: PostgreSQL8.1之前, idx_tup_read idx_tup_fetch计数实际上 总是一样的。现在即使是不考虑位图扫描,它们也可能是不同的,因为 idx_tup_read记录从索引检索的记录条目,而 idx_tup_fetch记录从表中抓取的活行数; 如果有已经失效的或者还未提交的行通过索引扫描找出来,后者将会小一些。

Thepg_statio_系列视图在判断缓冲区效果的时候特别有用。在实际磁盘读取‘ 远比缓冲命中小的时候,这个缓冲基本满足所有读要求,因此不需要进行内核调 用。但是,这些统计并未给出所有信息。由于PostgreSQL处理磁盘的方式,不 在PostgreSQL缓冲区中的数据可能仍然驻留在内核的I/O缓存中,因此仍然可 能不必经过物理读取。对获取PostgreSQL的I/O行为的更多细节感兴趣的用户 可以结合使用PostgreSQL的统计收集器和可以分析内核I/O处理的操作系统工 具来获取更多细节。

其它查看统计的方法可以通过书写使用底层统计访问函数的查询来设置, 这些底层统计访问函数和标准视图里使用的是一样的。这些函数在Table 27-2 中列出。针对某个数据库进行访问的函数接受一个数据库OID为参数来标 识需要报告哪个数据库。每个表和每个索引的功能 表或索引的OID。函数调用统计函数采取一个函数的OID。请注意这些函数只能看到在当前数据库里的表和索引。针 对某个服务器进行访问的函数接受一个服务器进程号, 其范围从1到当前活跃服务器的数目。

Table 27-2. 统计访问函数

函数返回类型描述
pg_stat_get_db_numbackends(o id)integer处理该数据库活跃的服务器进程数目
pg_stat_get_db_xact_commit(o id)bigint数据库中已提交事务数量目
pg_stat_get_db_xact_rollback(o id)bigint数据库中回滚的事务数量
pg_stat_get_db_blocks_fetched(o id)bigint数据库中磁盘块抓取请求的总数
pg_stat_get_db_blocks_hit(o id)bigint 为数据库在缓冲区中找到的磁盘块抓取请求的总数
pg_stat_get_db_tuples_returned(o id)bigint为数据库返回的Tuple数
pg_stat_get_db_tuples_fetched(o id)bigint为数据库中获取的Tuple数
pg_stat_get_db_tuples_inserted(o id)bigint在数据库中插入Tuple数
pg_stat_get_db_tuples_updated(o id)bigint在数据库中更新的Tuple数
pg_stat_get_db_tuples_deleted(o id)bigint数据库中删除Tuple数
pg_stat_get_numscans(o id)bigint 如果参数是一个表,那么就是顺序扫描读取的行数目, 如果参数是一个索引,那么就是返回的索引行的数目。
pg_stat_get_tuples_returned(o id)bigint 如果参数是一个表,那么就是顺序扫描读取的行数目,如果参数是一个索引,那么就是返回的索引行的数目。
pg_stat_get_tuples_fetched(o id)bigint 如果参数是一个表,那么就是位图扫描抓取的行数目,如果参数是一个索引,那么就是用简单索引扫描抓取的行数目。
pg_stat_get_tuples_inserted(o id)bigint插入表中的行数量
pg_stat_get_tuples_updated(o id)bigint在表中已更新的行数量,包括热更新
pg_stat_get_tuples_deleted(o id)bigint从表中删除的行数量
pg_stat_get_tuples_hot_updated(o id)bigint热更新的行数表
pg_stat_get_live_tuples(o id)bigint活行数表
pg_stat_get_dead_tuples(o id)bigint死行数表
pg_stat_get_blocks_fetched(o id)bigint表或者索引的磁盘块抓取请求的数量
pg_stat_get_blocks_hit(o id)bigint在缓冲区中找到的表或者索引的磁盘块请求数目
pg_stat_get_last_vacuum_time(o id)timestamptz用户在该表上最后一次启动清理的时间
pg_stat_get_last_autovacuum_time(o id)timestamptz autovacuum守护进程在该表上最后一次启动清理的时间
pg_stat_get_last_analyze_time(o id)timestamptz 用户在该表上最后一次启动分析的时间
pg_stat_get_last_autoanalyze_time(o id)timestamptz autovacuum守护进程在该表上最后一次启动分析的时间
pg_backend_p id()integer 当前会话的服务器进程的进程ID
pg_stat_get_activity(integer)setofrecord返回一个关于带有特殊PID号的后台进程的记录信息,或者是每个 活动的后台进程的记录(如果声明了NULL)。 返回结果是pg_stat_activity视图中的一个子集。
pg_stat_get_function_calls(o id)bigint 函数已被调用次数
pg_stat_get_function_time(o id)bigint 总挂钟时间花费在功能,在微秒的。包括 在这个函数调用所花费的时间。
pg_stat_get_function_self_time(o id)bigint 只有在此功能所花费的时间。在所谓的功能所花费的时间 被排除在外。
pg_stat_get_backend_ idset()setofinteger 设置当前活动的服务器进程数(从1到 活动服务器进程的数量)。看在文本中使用的例子。
pg_stat_get_backend_p id(integer)integer给定的服务器进程的PID
pg_stat_get_backend_db id(integer)o id给定的服务器进程的数据库ID
pg_stat_get_backend_user id(integer)o id给定的服务器进程的用户ID
pg_stat_get_backend_activity(integer)text 给定服务器进程的当前活动查询,仅在调用者是超级用户或被查询会话的用户, 并且打开track_activities的时候才能获得结果。
pg_stat_get_backend_waiting(integer)boolean 如果给定服务器进程在等待某个锁,并且调用者是超级用户或被查询会话的用户, 并且打开track_activities的时候才返回真。
pg_stat_get_backend_activity_start(integer)timestampwithtimezone 给定服务器进程当前正在执行的查询的起始时间,仅在调用者是超级用户或被查询会话的用户, 并且打开track_activities的时候才能获得结果。
pg_stat_get_backend_xact_start(integer)timestampwithtimezone给定的服务进程当前正在执行的事务的开始时间,但只有 当权用户是超级用户或正在被插叙的会话的相同用户(同时 ,开启track_activities)才可以使用。
pg_stat_get_backend_start(integer)timestampwithtimezone timezone给定服务器进程启动的时间,如果当前用户不是超级用户或被查询的后端的用户,则返回NULL。
pg_stat_get_backend_client_addr(integer)inet 连接到给定服务器进程的客户端IP地址。如果是通过Unix域套接字连接的则返回NULL。 如果当前用户不是超级用户或被查询会话的用户,也返回NULL。
pg_stat_get_backend_client_port(integer)integer 连接到给定服务器进程的客户端IP端口。如果是通过Unix域套接字连接的则返回-1。 如果当前用户不是超级用户或被查询会话的用户,也返回NULL。
pg_stat_get_bgwriter_timed_checkpoints()bigint后台写进程开启定时检查点的时间(因为 checkpoint_timeout时间已经过期了)
pg_stat_get_bgwriter_requested_checkpoints()bigint后台写进程开启基于后端请求的检查点的时间,因为已经超过了checkpoint_segments 或因为已经执行了CHECKPOINT
pg_stat_get_bgwriter_buf_written_checkpoints()bigint在检查点期间后台写进程写入的缓冲区数目。
pg_stat_get_bgwriter_buf_written_clean()bigint为日常清理脏块,后台写进程写入的缓冲区数目。
pg_stat_get_bgwriter_maxwritten_clean()bigint后台写进程停止清理扫描的时间,因为 已经写入了更多的缓冲区(相比bgwriter_lru_maxpages 参数声明的缓冲区数)。
pg_stat_get_buf_written_backend()bigint后端进程写入的缓冲区数,因为它们需要分配一个新的缓冲区。
pg_stat_get_buf_alloc()bigint分配的总缓冲区数。
pg_stat_clear_snapshot()void忽略当前的统计快照。
pg_stat_reset()void为当前数据库重置统计计数器为0(需要超级用户权限)。
pg_stat_reset_shared(text)void为当前数据库集群重置共享统计计数器为0(需要超级用户权限)。 调用pg_stat_reset_shared('bgwriter')将所有 pg_stat_bgwriter显示的值置为0。
pg_stat_reset_single_table_counters(o id)void为当前数据库中的一个表或索引重置统计为0(需要超级用户权限)。
pg_stat_reset_single_function_counters(o id)void为当前数据库中的一个函数重置统计为0(需要超级用户权限)。

Note: pg_stat_get_blocks_fetched减去pg_stat_get_blocks_hit就是为该表、索引、数据库而调用 内核read()函数的数目;不过实际的物理读取的数目通常比较低, 因为还有内核级的缓冲。*_blks_read用减法统计列获取减去结果

后端ID的所有功能,访问有关后端的信息索引数,除了pg_stat_get_activity,这是由PID功能。 pg_stat_get_backend_ idset函数提供了为每个活跃服务器进程生成一行的便捷方法。 比如,要显示所有服务器进程的PID和它们的当前查询:

SELECTpg_stat_get_backend_p id(s.backend id)ASprocp id,
pg_stat_get_backend_activity(s.backend id)AScurrent_query
FROM(SELECTpg_stat_get_backend_ idset()ASbackend id)ASs;