可以使用一個非常簡單的資料集來演示多元相關性,即一個具有兩個欄位的表格,兩個欄位都包含相同的值
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 個不同的值,且均勻分佈。
以下範例顯示了估計 a
欄位上的 WHERE
條件的結果
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
當估計多個欄位的集合的基數時,例如由 GROUP BY
子句產生的群組數時,會發生類似的問題。當 GROUP BY
列出單個欄位時,n-相異估計值(顯示為 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)
透過重新定義統計資訊物件以包含兩個欄位的 n-相異計數,估計值會大大提高
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)
如 第 68.2.1 節 中所述,函數相依性是非常便宜且高效的統計資訊類型,但它們的主要限制是它們的整體性質(僅追蹤欄位層級的相依性,而不是個別欄位值之間的相依性)。
本節介紹了多元變體MCV(最常見值) 清單,是 第 68.1 節 中描述的每個欄位統計資訊的直接擴展。這些統計資訊透過儲存個別值來解決此限制,但自然地,在 ANALYZE
中建立統計資訊、儲存和規劃時間方面,成本更高。
讓我們再次看一下 第 68.2.1 節 中的查詢,但這次是使用在同一組欄位上建立的MCV清單(請務必刪除函數相依性,以確保規劃器使用新建立的統計資訊)。
DROP STATISTICS stts; CREATE STATISTICS stts2 (mcv) 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
估計值與函數相依性的估計值一樣準確,這主要歸功於表格相當小,並且具有具有少量不同值的簡單分佈。在查看第二個查詢(函數相依性無法很好地處理)之前,讓我們先檢查一下MCV清單。
可以使用 pg_mcv_list_items
設定傳回函數來檢查MCV清單。
SELECT m.* FROM pg_statistic_ext join pg_statistic_ext_data on (oid = stxoid), pg_mcv_list_items(stxdmcv) m WHERE stxname = 'stts2'; index | values | nulls | frequency | base_frequency -------+----------+-------+-----------+---------------- 0 | {0, 0} | {f,f} | 0.01 | 0.0001 1 | {1, 1} | {f,f} | 0.01 | 0.0001 ... 49 | {49, 49} | {f,f} | 0.01 | 0.0001 50 | {50, 50} | {f,f} | 0.01 | 0.0001 ... 97 | {97, 97} | {f,f} | 0.01 | 0.0001 98 | {98, 98} | {f,f} | 0.01 | 0.0001 99 | {99, 99} | {f,f} | 0.01 | 0.0001 (100 rows)
這證實了這兩個欄位中有 100 個不同的組合,並且它們都同樣可能(每個組合的頻率為 1%)。基本頻率是從每個欄位的統計資訊計算得出的頻率,就像沒有多欄統計資訊一樣。如果任一欄位中有任何空值,則會在 nulls
欄位中識別出來。
在估計選擇性時,規劃器會將所有條件套用到MCV清單中的項目,然後將符合項目的頻率加總。請參閱 src/backend/statistics/mcv.c
中的 mcv_clauselist_selectivity
以取得詳細資訊。
與函數相依性相比,MCV清單有兩個主要優點。首先,該清單儲存實際值,可以決定哪些組合相容。
EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 10; QUERY PLAN --------------------------------------------------------------------------- Seq Scan on t (cost=0.00..195.00 rows=1 width=8) (actual rows=0 loops=1) Filter: ((a = 1) AND (b = 10)) Rows Removed by Filter: 10000
其次,MCV清單處理更廣泛的子句類型,而不僅僅是像函數相依性這樣的等式子句。例如,考慮一下同一個表格的以下範圍查詢
EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a <= 49 AND b > 49; QUERY PLAN --------------------------------------------------------------------------- Seq Scan on t (cost=0.00..195.00 rows=1 width=8) (actual rows=0 loops=1) Filter: ((a <= 49) AND (b > 49)) Rows Removed by Filter: 10000
如果您在文件中發現任何不正確、與您對特定功能的體驗不符或需要進一步說明的內容,請使用 此表單 報告文件問題。