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 不是实时更新的, 因此它们通常包含可能有些过时的数值。它们被VACUUM, ANALYZE和几个DDL命令 (比如CREATE INDEX)更新。 VACUUM或者ANALYZE操作不扫描整个表(这是常见的情况), 将逐步基于扫描表的部分更新reltuples数,从而产生一个近似值。 在任何情况下,规划器将把pg_class表里面的数值调整为和当前的物理表尺寸匹配, 以此获取一个更接近的近似值。

大多数查询只是检索表中行的一部分,因为它们有限制待查行的WHERE子句。 因此规划器需要对WHERE子句的选择性进行评估, 选择性也就是符合WHERE子句中每个条件的部分。 用于这个目的的信息存储在pg_statistic系统表中。 在pg_statistic中的记录是由ANALYZEVACUUM ANALYZE命令更新的, 并且总是近似值,即使刚刚更新完也不例外。

除了直接查看pg_statistic之外, 我们手工检查统计的时候最好查看pg_stats的视图。 pg_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)。

通过ANALYZE命令存储在pg_statistic中的信息的数量, 特别是给每个字段用的most_common_valshistogram_bounds数组上的最大记录数目 可以用ALTER TABLE SET STATISTICS命令设置, 或者是用运行时参数default_statistics_target进行全局设置。 目前缺省的限制值是 100个记录。 增加该限制值应该可以做出更准确的规划器估计,特别是对那些有不规则数据分布的字段而言, 代价是在pg_statistic里使用了更多空间,并且需要略微多一些的时间计算估计数值。相比之下, 比较低的限制可能更适合那些数据分布比较简单的字段。

有关规划器使用统计信息的进一步详情可参阅第 60 章