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

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

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

regression=# SELECT relname, relkind, reltuples, relpages FROM pg_class
regression-# WHERE relname like 'tenk1%';
    relname    | relkind | reltuples | relpages
---------------+---------+-----------+----------
 tenk1         | r       |     10000 |      233
 tenk1_hundred | i       |     10000 |       30
 tenk1_unique1 | i       |     10000 |       30
 tenk1_unique2 | i       |     10000 |       30
(4 rows)

我们在这里可以看到 tenk1 有 10000 行, 它的索引也有这么多行,但是索引远比表小得多(很正常).

出于效率考虑,reltuplesrelpages 不是实时更新的, 因此它们通常包含近似的数值(对于规划器使用而言是足够好了). 在表创建时,它们初始化为一些假值(目前分别是 1000 和 10). 然后由一些命令更新,目前是 VACUUMANALYZE, 和 CREATE INDEX.一个独立的 ANALYZE (也就是没有和 VACUUM 在一起的),生成一个 reltuples 的近似数值, 因为它并没有读取表里的每一行.

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

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

regression=# 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)
regression=#

Table 10-1 显示了存在于 pg_stats 的字段。

Table 10-1. pg_stats字段

名称类型描述
tablenamename包含这些行的表的名称
attnamename本行描述的字段名称
null_fracreal该字段中为 NULL 的记录比例
avg_widthinteger以字节计的该字段记录的的平均宽度
n_distinctreal如果大于零,它是字段中唯一数值的估计数. 如果小于零,是唯一数值被总行数除的负数. (在ANALYZE认为唯一数值会随着表的增长而增长的时候采用负数形式; 而在该字段看上去好像有固定的可能数值的情况下使用正数形式.) 比如,-1 表示一个唯一的字段,它的唯一数值和行数相同.
most_common_valstext[]字段中最常用数值的一个列表. (如果看上去没有什么数值比其他数值更常见,那么省略)
most_common_freqsreal[]最常用数值的一个频率列表,也就是说, 每个常见值被总行数除.
histogram_boundstext[]一列数值,这些数值把该字段分割成近似相等的几组. 如果存在 most_common_vals,那么在 频度计算中忽略.(如果字段的数据类型没有 < 操作符,或者 most_common_vals 列表 负责全部的记录,那么这个列表也被忽略.)
correlationreal字段数值的物理行顺序和逻辑顺序之间的统计相关性. 其范围从 -1 到 +1.如果数值接近 -1 或者 +1,认为 比起数值在零附近时,在该字段上的 索引扫描预计会更经济一些,这是对磁盘的随机访问的推论. (如果字段数据类型没有 < 操作符则省略.)

most_common_valshistogram_bounds 数组上的最大记录数目可以用 ALTER TABLE SET STATISTICS 命令 以字段为基础逐一设置.目前缺省的限制是 10 个记录. 提升该限制应该可以做出更准确的规划器估计,特别是对那些有不规则 数据分布的字段而言,付出的代价是在 pg_statistic 里使用了更多空间,并且需要略微多一些的时间计算估计数值. 相比之下,比较低的限制可能更适合那些数据分布比较简单的字段.