↑↓ 选择↵ 打开⌫ 切换范围完整搜索

PG.CENTER 连接 PostgreSQL 文档、百科与生态知识。由 Pigsty 维护。

支持中的版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发中的版本: 19 / 20devel
已结束支持的版本: 13 / 12 / 11 / 10
历史版本。 PostgreSQL 10 已结束支持。 2022-11-10. 请参阅 当前版本手册.

69.2. 多元统计信息示例 #

69.2.1. 函数依赖

多元相关性可以通过一个非常简单的数据集来演示 — 一个包含两列的表,两列都含有相同的值:

CREATE TABLE t (a INT, b INT);
INSERT INTO t SELECT i % 100, i % 100 FROM generate_series(1, 10000) s(i);
ANALYZE t;

如第 14.2 节所述,规划器可以利用从 pg_class 得到的页数和行数来确定 t 的基数:

SELECT relpages, reltuples FROM pg_class WHERE relname = 't';

 relpages | reltuples
----------+-----------
       45 |     10000

数据分布非常简单;每一列中都只有 100 个不同的值,并且均匀分布。

下面的示例展示了对 WHERE 条件在 a 列上进行估计的结果:

EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1;
                                 QUERY PLAN                                  
-------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..170.00 rows=100 width=8) (actual rows=100 loops=1)
   Filter: (a = 1)
   Rows Removed by Filter: 9900

规划器会检查该条件,并认定此子句的选择率为 1%。将该估计与实际行数相比,可以看出估计非常准确(实际上是精确的,因为这张表非常小)。把 WHERE 条件改为使用 b 列,会生成相同的计划。但请看如果把同一条件同时应用到两列上,并用 AND 连接,会发生什么:

EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1;
                                 QUERY PLAN                                  
-----------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..195.00 rows=1 width=8) (actual rows=100 loops=1)
   Filter: ((a = 1) AND (b = 1))
   Rows Removed by Filter: 9900

规划器分别估计每个条件的选择率,得到与上面相同的 1% 估计值。然后它假定这些条件彼此独立,于是将它们的选择率相乘,得到最终只有 0.01% 的选择率估计。这是严重的低估,因为实际匹配这些条件的行数(100)要高出两个数量级。

这个问题可以通过创建一个统计信息对象来解决,该对象会指示 ANALYZE 在这两列上计算函数依赖的多元统计信息:

CREATE STATISTICS stts (dependencies) ON a, b FROM t;
ANALYZE t;
EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1;
                                  QUERY PLAN                                   
-------------------------------------------------------------------------------
 Seq Scan on t  (cost=0.00..195.00 rows=100 width=8) (actual rows=100 loops=1)
   Filter: ((a = 1) AND (b = 1))
   Rows Removed by Filter: 9900

69.2.2. 多元非重复值计数

在估计多列集合的基数时,也会出现类似的问题,例如 GROUP BY 子句会生成多少个组。当 GROUP BY 只列出一列时,非重复值数量估计(可从 HashAggregate 节点估计返回的行数看出)非常准确:

EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a;
                                       QUERY PLAN                                        
-----------------------------------------------------------------------------------------
 HashAggregate  (cost=195.00..196.00 rows=100 width=12) (actual rows=100 loops=1)
   Group Key: a
   ->  Seq Scan on t  (cost=0.00..145.00 rows=10000 width=4) (actual rows=10000 loops=1)

但是如果没有多元统计信息,对于在 GROUP BY 中包含两列的查询,组数估计会像下面这样差一个数量级:

EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a, b;
                                       QUERY PLAN                                        
--------------------------------------------------------------------------------------------
 HashAggregate  (cost=220.00..230.00 rows=1000 width=16) (actual rows=100 loops=1)
   Group Key: a, b
   ->  Seq Scan on t  (cost=0.00..145.00 rows=10000 width=8) (actual rows=10000 loops=1)

通过重新定义统计信息对象,使其包含这两列的非重复值计数,估计值就会大幅改进:

DROP STATISTICS stts;
CREATE STATISTICS stts (dependencies, ndistinct) ON a, b FROM t;
ANALYZE t;
EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a, b;
                                       QUERY PLAN                                        
--------------------------------------------------------------------------------------------
 HashAggregate  (cost=220.00..221.00 rows=100 width=16) (actual rows=100 loops=1)
   Group Key: a, b
   ->  Seq Scan on t  (cost=0.00..145.00 rows=10000 width=8) (actual rows=10000 loops=1)

报告文档问题

阅读 上游文档. 反馈更正前请先核对 当前版本手册.