23.2. 统计收集器

PostgreSQL统计收集器是一个支持收集和汇报服务器活跃性信息的子系统。 目前,这个收集器可以给对表和索引的访问计数,包括磁盘块的数量和独立行的项。 它还可以判断当前其它服务器进程在执行的命令是什么。

23.2.1. 统计收集器配置

因为统计收集给查询处理增加了一些过热,所以你可以把系统配置为收集信息,也可以配置为不收集信息。 这个是由配置参数控制的,这些配置参数通常在 postgresql.conf 里设置(参阅 Section 16.4 获取有关设置配置参数的细节)。

要想让统计收集器运行起来, 参数 stats_start_collector 必须设置为真。 这个设置是缺省设置,也是建议设置,但是如果你对统计不感兴趣并且想把所有过荷都挤出去, 那么可以把它设置为假。(不过,省下来的东西并不多。) 请注意这个选项在服务器运行的时候并不能改变。

参数 stats_command_stringstats_block_level,和 stats_row_level 控制实际发送给收集器的数量, 因此也决定了会产生多少运行时过热。这些选项分别决定一个服务器进程是否发送它的当前命令字串, 磁盘块层次的访问统计,以及行层次的访问统计给收集器。 通常这些参数在 postgresql.conf 中设置, 因此它们适用于所有服务器进程, 但是我们也可以在独立的会话里用 SET 命令把它们打开或者关闭。 (为避免普通用户把它们的活跃性隐藏不给管理员看,只有超级用户允许用 SET 命令修改这些参数。)

注意: 因为参数 stats_command_stringstats_block_level,和 stats_row_level 缺省时false, 索引实际上缺省配置中不收集任何统计信息。 打开这些配置变量中的一个或多个可以显著增加统计收机器生成的有用信息的数量,代价是增加了一点运行时开销。

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

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

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

另外一个需要着重指出的问题是,在请求服务器进程显示任何这些统计信息的时候, 它首先抓取收集器进程发出的最新的报告。然后它就接着拿这些数据作为所有统计视图和函数的快照, 直到它当前的事务的结束。 因此统计在你当前事务的持续期间内时不会改变的。这是一个特性, 而不是一个毛病,因为这样就允许你在统计上执行几个查询并且对结果进行相关性的检查而又不用担心这些数字会背着你变化。 但是如果你想看每个查询的新的结果,那么就要记住在任何事务块外面处理这些查询。

Table 23-1. 标准统计视图

视图名字描述
pg_stat_activity每个服务器进程一行,显示进程ID,数据库,用户,当前查询和当前查询开始执行的时间。 只有在打开 stats_command_string 参数的时候, 才能得到报告当前查询的相关信息的各个字段。 另外,除非检查这些字段的用户是超级用户或者是拥有正在汇报的进程的同一个用户,否则它们显示为空。 (请注意因为收集器的报告延迟,当前查询只是对长时间运行的查询及时更新。)
pg_stat_database每个数据库一行,显示激活的后端的数量,提交的事务总数以及在该数据库中回卷数目的总数, 读取的磁盘块的总数,以及缓冲区命中的总数(也就是中所需要的块已经在缓冲区中找到,从而避免了读取块的动作)。
pg_stat_all_tables当前数据库中每个表一行,里面有顺序扫描和索引扫描的总数, 每种类型的扫描返回的元组的总数以及元组插入,更新,和删除的总数。
pg_stat_sys_tablespg_stat_all_tables一样,只不过只显示系统表。
pg_stat_user_tablespg_stat_all_tables一样,只不过只显示用户表。
pg_stat_all_indexes当前数据库的每个索引一行,包括使用了该索引的索引扫描总数, 索引元组读取的总数以及成功抓取的堆元组的数目(如果有些索引记录指向过期的堆元组,那么这个数目可能少一些。)
pg_stat_sys_indexespg_stat_all_indexes一样,只不过只包含那些显示为系统表上的索引。
pg_stat_user_indexespg_stat_all_indexes一样,只不过只包含那些显示为用户表上的索引。
pg_statio_all_tables当前数据库中每个表一行,包含从该表中读取的磁盘块总数, 缓冲区命中的总数,在该表上所有索引的磁盘块读取和缓冲区命中总数, 在该表的辅助 TOAST 表(如果存在)上的磁盘块读取和缓冲区命中总数, 以及 TOAST 表的索引的磁盘块读取和缓冲区命中总数.
pg_statio_sys_tablespg_statio_all_tables一样,只不过只显示系统表.
pg_statio_user_tablespg_statio_all_tables一样,只不过只显示用户表.
pg_statio_all_indexes当前数据库中每个索引一行,包含该索引的磁盘块读取和缓冲区命中的数目。
pg_statio_sys_indexespg_statio_all_indexes一样,只不过只显示系统表。
pg_statio_user_indexespg_statio_all_indexes一样,只不过只显示用户表。
pg_statio_all_sequences当前数据库中每个序列对象一个,包含该序列磁盘读取和缓冲区命中的数目。
pg_statio_sys_sequencespg_statio_all_sequences一样,只不过只显示系统序列。 (准确地说,我们没有定义系统序列,所以这个视图总是空的。)
pg_statio_user_sequencespg_statio_all_sequences一样,只不过只显示用户序列。

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

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

其它查看统计的方法可以通过书写使用下层统计访问函数的查询来设置,这些下层统计访问函数和标准视图里使用的是一样的。 这些函数在Table 23-2 中列出。 就某数据库进行访问的函数接受一个数据库 OID 为参数来标识需要报告哪个数据库。 就某表或者某索引进行访问的函数接受一个表或者索引的 OID。 (请注意这些函数只能看到在当前数据库里的表和索引)。 就某后端进行访问的函数接受一个后端 ID 号,其范围从一到当前活跃后端的数目。

Table 23-2. 统计访问函数

函数返回类型描述
pg_stat_get_db_numbackends(oid)integer 处理数据库的活跃的后端进程数目。
pg_stat_get_db_xact_commit(oid)bigint 数据库中已提交事务数量。
pg_stat_get_db_xact_rollback(oid)bigint 数据库中回卷的事务数量
pg_stat_get_db_blocks_fetched(oid)bigint 数据库中磁盘块抓取请求总数
pg_stat_get_db_blocks_hit(oid)bigint 为数据库在缓冲区中找到的磁盘块抓取请求总数
pg_stat_get_numscans(oid)bigint 如果参数是一个表,那么就是进行的顺序扫描的数目, 如果是一个索引,那么就是索引扫描的数目。
pg_stat_get_tuples_returned(oid)bigint 如果参数是一个表,那么就是顺序扫描读取的元组数目, 如果是一个索引,那么就是索引元组的数目
pg_stat_get_tuples_fetched(oid)bigint 如果参数是一个表,那么就是顺序扫描抓取的有效(未过期)的表元组数目, 如果是一个索引,那么就是用这个索引抓取的有效表元组数目
pg_stat_get_tuples_inserted(oid)bigint 插入表中的元组数量
pg_stat_get_tuples_updated(oid)bigint 在表中已更新的元组数量
pg_stat_get_tuples_deleted(oid)bigint 从表中删除的元组数量
pg_stat_get_blocks_fetched(oid)bigint 表或者索引的磁盘块抓取请求的数量
pg_stat_get_blocks_hit(oid)bigint 在缓冲区中找到的表或者索引的磁盘块请求数目
pg_stat_get_backend_idset()set of integer 当前活跃后端 ID 的集合(从 1 到活跃后端的数目)。 参阅文本中的使用样例。
pg_backend_pid()integer 附着在当前会话上的后端进程 ID
pg_stat_get_backend_pid(integer)integer 给出的后端进程的进程号
pg_stat_get_backend_dbid(integer)oid 指定后端进程的数据库 ID
pg_stat_get_backend_userid(integer)oid 指定后端进程的用户 ID
pg_stat_get_backend_activity(integer)text 后端进程的当前活跃查询(如果调用者不是超级用户,或者不是与被查询的会话同一个用户, 或者没有打开 stats_command_string 则为 NULL)
pg_stat_get_backend_activity_start(integer)timestamp with time zone 指定后端进程当前正在执行的查询的起始时间(如果当前用户不是超级用户,或者 不是与被查询的会话同一个用户,或者没有打开 stats_command_string 则为 NULL)
pg_stat_reset()boolean 重置所有当前收集的统计。

注意: pg_stat_get_db_blocks_fetched 减去 pg_stat_get_db_blocks_hit 就是为该表,索引或者数据库 发出的 read() 内核调用的数目;不过实际的物理读取的数目通常比较低, 因为还有内核级的缓冲。

函数 pg_stat_get_backend_idset 提供了一个为每个活跃后端进程生成一行的便捷的方法。 比如,要显示所有后端的PID和它们的当前查询:

SELECT pg_stat_get_backend_pid(s.backendid) AS procpid,
       pg_stat_get_backend_activity(s.backendid) AS current_query
    FROM (SELECT pg_stat_get_backend_idset() AS backendid) AS s;