规划器使用的统计信息

就像我们在上一节里展示的那样,查询规划器需要估计一个查询检索的行数, 这样才能选择正确的查询规划。本节就系统用于这些估计的统计进行一些描述。

统计的一个部分就是每个表和索引中的记录总数,以及每个表和索引占据的磁盘块数。 这个信息保存在pg_class表的reltuples和relpages字段中。 我们可以用类似下面的查询检索这些信息:

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

       relname        | relkind | reltuples | relpages
----------------------+---------+-----------+----------
 tenk1                | r       |     10000 |      358

出于效率考虑,reltuples和relpages 不是实时更新的, 因此它们通常包含可能有些过时的数值。它们被VACUUM, ANALYZE和几个DDL命令更新。独立的ANALYZE(不属于VACUUM的一部分)生成近似的reltuples值,因为它不读取表的每一行。规划器将缩放它在pg_class中找到的值以匹配当前的物理表大小,从而获得更接近的近似值。

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

除了直接查看pg_statistic之外, 我们手工检查统计的时候最好查看pg_stats的视图。 pg_stats被设计成有更好的可读性。 而且,pg_stats是所有人都可以读取的,而pg_statistic只能由超级用户读取。 这样就可以避免非特权用户从统计信息中获取一些和其他人的表内容相关的信息。pg_stats视图是受约束的, 只显示当前用户可读的表。比如,我们可以:

SELECT attname, n_distinct, most_common_vals FROM pg_stats WHERE tablename = 'road';

 attname | n_distinct |                                                                                                                                                                                  most_common_vals
---------+------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 name    |  -0.467008 | {"I- 580                        Ramp","I- 880                        Ramp","Sp Railroad                       ","I- 580                            ","I- 680                        Ramp","I- 80                         Ramp","14th                          St  ","5th                           St  ","Mission                       Blvd","I- 880                            "}
 thepath |         20 | {"[(-122.089,37.71),(-122.0886,37.711)]"}
(2 rows)

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