14.2. 规划器使用的统计信息

如我们在上一节所见,查询规划器需要估计一个查询要检索的行数,这样才能对查询计划做出好的选择。 本节对系统用于这些估计的统计信息进行一个快速的介绍。

统计信息的一个部分就是每个表和索引中的项的总数,以及每个表和索引占用的磁盘块数。这些信息保存在pg_class表的reltuplesrelpages列中。 我们可以用类似下面的查询查看这些信息:

SELECT relname, relkind, reltuples, relpages
FROM pg_class
WHERE relname LIKE 'tenk1%';

       relname        | relkind | reltuples | relpages
----------------------+---------+-----------+----------
 tenk1                | r       |     10000 |      358
 tenk1_hundred        | i       |     10000 |       30
 tenk1_thous_tenthous | i       |     10000 |       30
 tenk1_unique1        | i       |     10000 |       30
 tenk1_unique2        | i       |     10000 |       30
(5 rows)

这里我们可以看到tenk1包含 10000 行, 它的索引也有这么多行,但是索引远比表小得多(不奇怪)。

出于效率考虑,reltuplesrelpages不是实时更新的 ,因此它们通常包含有些过时的值。它们被VACUUMANALYZE和几个 DDL 命令(例如CREATE INDEX)更新。一个不扫描全表的VACUUMANALYZE操作(常见情况)将以它扫描的部分为基础增量更新reltuples计数,这就导致了一个近似值。在任何情况中,规划器将缩放它在pg_class中找到的值来匹配当前的物理表尺寸,这样得到一个较紧的近似。

大多数查询只是检索表中行的一部分,因为它们有限制要被检查的行的WHERE子句。 因此规划器需要估算WHERE子句的选择度,即符合WHERE子句中每个条件的行的比例。 用于这个任务的信息存储在pg_statistic系统目录中。 在pg_statistic中的项由ANALYZEVACUUM ANALYZE命令更新, 并且总是近似值(即使刚刚更新完)。

除了直接查看pg_statistic之外, 手工检查统计信息的时候最好查看它的视图pg_statspg_stats被设计为更容易阅读。 而且,pg_stats是所有人都可以读取的,而pg_statistic只能由超级用户读取(这样可以避免非授权用户从统计信息中获取一些其他人的表的内容的信息。pg_stats视图被限制为只显示当前用户可读的表)。例如,我们可以:

SELECT attname, inherited, n_distinct,
       array_to_string(most_common_vals, E'\n') as most_common_vals
FROM pg_stats
WHERE tablename = 'road';

 attname | inherited | n_distinct |          most_common_vals
---------+-----------+------------+------------------------------------
 name    | f         |  -0.363388 | I- 580                        Ramp+
         |           |            | I- 880                        Ramp+
         |           |            | Sp Railroad                       +
         |           |            | I- 580                            +
         |           |            | I- 680                        Ramp
 name    | t         |  -0.284859 | I- 880                        Ramp+
         |           |            | I- 580                        Ramp+
         |           |            | I- 680                        Ramp+
         |           |            | I- 580                            +
         |           |            | State Hwy 13                  Ramp
(2 rows)

注意,这两行显示的是相同的列,一个对应开始于road表(inherited=t)的完全继承层次, 另一个只包括road表本身(inherited=f)。

ANALYZEpg_statistic中存储的信息量(特别是每个列的most_common_vals中的最大项数和histogram_bounds数组)可以用ALTER TABLE SET STATISTICS命令为每一列设置, 或者通过设置配置变量default_statistics_target进行全局设置。 目前的默认限制是 100 个项。提升该限制可能会让规划器做出更准确的估计(特别是对那些有不规则数据分布的列), 其代价是在pg_statistic中消耗了更多空间,并且需要略微多一些的时间来计算估计数值。 相比之下,比较低的限制可能更适合那些数据分布比较简单的列。

更多规划器对统计信息的使用可参阅Chapter 67