PostgreSQL爲什麼允許用戶自定義統計信息

PostgreSQL10開始支持使用create statistics來自定義統計信息。那麼爲什麼pg允許用戶自己去定義統計信息呢?
首先我們要知道在數據庫中統計信息是什麼?統計信息主要做的事情就是:收集數據庫中對象的詳細信息,並存儲在相應的數據字典裏。
因爲pg的優化器是基於成本的估算,而成本估算中很重要的一個環節是估計每個執行節點返回的記錄數,因此統計信息的正確與否和執行計劃息息相關。
雖然在大多數情況下,pg中的統計信息還是很準確的,但是對於多個字段,PostgreSQL默認使用獨立屬性,直接以多個字段選擇性相乘的方法計算多個字段條件的選擇性。不是很準確。這個時候我們就可以使用自定義統計信息來解決這種問題。

語法:

CREATE STATISTICS [ IF NOT EXISTS ] statistics_name
    [ ( statistics_kind [, ... ] ) ]
    ON column_name, column_name [, ...]
    FROM table_name

例子:
–創建測試表
我們創建一張很簡單的測試表,兩個字段的值都是1到100,每個值重複100次,均勻分佈。

bill=# create table test(c1 int,c2 int);
CREATE TABLE
bill=# insert into test select i%100,i%100 from generate_series(1,10000) s(i);
INSERT 0 10000
bill=# analyze test;
ANALYZE

接下來我們在c1列上進行查詢:
可以看到執行計劃準確無誤(c1=1的記錄的確是100條)。

bill=# explain (analyze,timing off) select * from test where c1 = 1;                
                                    QUERY PLAN                                    
----------------------------------------------------------------------------------
 Seq Scan on test  (cost=0.00..170.00 rows=100 width=8) (actual rows=100 loops=1)
   Filter: (c1 = 1)
   Rows Removed by Filter: 9900
 Planning Time: 0.056 ms
 Execution Time: 0.756 ms
(5 rows)

接着我們進行多列查詢:
此時優化器生成的執行計劃產生最終選擇性估計值僅爲0.01%(即1條記錄),而實際上滿足c1=1 and c2=1的數據有100條。這是一個明顯的低估,因爲符合條件(100)的實際行數要高於兩個數量級。

bill=# explain analyze select * from test where c1 = 1 and c2 = 1;
                                            QUERY PLAN                                            
--------------------------------------------------------------------------------------------------
 Seq Scan on test  (cost=0.00..195.00 rows=1 width=8) (actual time=0.013..0.811 rows=100 loops=1)
   Filter: ((c1 = 1) AND (c2 = 1))
   Rows Removed by Filter: 9900
 Planning Time: 0.068 ms
 Execution Time: 0.833 ms
(5 rows)

bill=# select count(*) from test where c1 = 1 and c2 = 1;
 count 
-------
   100
(1 row)

對於這種情況,我們就可以使用自定義統計信息來解決:

bill=# CREATE STATISTICS sta1 (dependencies) ON c1, c2 FROM test;
CREATE STATISTICS
bill=# analyze test;
ANALYZE

然後再觀察剛剛查詢的執行計劃:
現在優化器估計的行數是100行了,和實際情況一致!

bill=# explain analyze select * from test where c1 = 1 and c2 = 1;        
                                             QUERY PLAN                                             
----------------------------------------------------------------------------------------------------
 Seq Scan on test  (cost=0.00..195.00 rows=100 width=8) (actual time=0.011..0.834 rows=100 loops=1)
   Filter: ((c1 = 1) AND (c2 = 1))
   Rows Removed by Filter: 9900
 Planning Time: 0.158 ms
 Execution Time: 0.857 ms
(5 rows)

除此之外,估計多列集合的基數時也會出現類似的問題,例如由GROUP BY 子句生成的組的數量。當GROUP BY列出單個列時, n個不同估計值(作爲HashAggregate節點返回的估計行數可見)非常準確。因此pg允許用戶自定義統計信息顯得十分重要。

參考資料:
https://www.postgresql.org/docs/13/row-estimation-examples.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章