您所不瞭解的Postgres 10功能:CREATE STATISTICS(譯)

来源:https://www.cnblogs.com/wy123/archive/2020/07/15/13306673.html
-Advertisement-
Play Games

原文地址:https://www.citusdata.com/blog/2018/03/06/postgres-planner-and-its-usage-of-statistics/,本文統一將原文中的“planner”譯做“優化器” 如果您對Postgres進行了一些性能優化,則可能使用過EXP ...


  原文地址:https://www.citusdata.com/blog/2018/03/06/postgres-planner-and-its-usage-of-statistics/,本文統一將原文中的“planner”譯做“優化器”       如果您對Postgres進行了一些性能優化,則可能使用過EXPLAIN。 EXPLAIN向您顯示PostgreSQL計劃程式為提供的語句生成的執行計劃。 它顯示瞭如何掃描語句引用的表(使用順序掃描,索引掃描等),以及如果使用多個表,將使用哪種聯接演算法。但是,Postgres是依據什麼信息給出執行計劃的?
優化器(planner)收集統計數據是決定使用哪種計劃的非常重要的參考信息。 這些統計信息使優化器(planner)可以估計執行計劃的特定部分後將返回多少行,這將影響執行計劃將要使用的掃描或聯接演算法的類型。 統計主要通過運行ANALYZE或VACUUM(以及一些DDL命令,如CREATE INDEX)來收集/更新它們。
這些統計信息由存儲在pg_class和pg_statistics中。  Pg_class基本上存儲每個表和索引中的條目總數,以及它們所占用的磁碟塊數。  Pg_statistic存儲有關每個列的統計信息,例如該列的值的為空的百分比,最常見的值是什麼,直方圖範圍等。 您可以在下麵的表格中查看以下示例,該示例針對針對col1收集的Postgres統計類型。 下麵的查詢輸出顯示,planner(正確)估計表中的col1列有1000個不同的值,並且還對最常見的值,頻率等進行其他估計。
請註意,我們已經查詢了pg_stats(該視圖保存了更易讀的列統計信息。)
CREATE TABLE tbl (                                                                        
    col1 int,                                                                             
    col2 int                                                                              
);                                                                                        

INSERT INTO tbl SELECT i/10000, i/100000                                                  
FROM generate_series (1,10000000) s(i);                                                   

ANALYZE tbl;                                     

select * from pg_stats where tablename = 'tbl' and attname = 'col1';
-[ RECORD 1 ]----------+--------------------------------
schemaname             | public
tablename              | tbl
attname                | col1
inherited              | f
null_frac              | 0
avg_width              | 4
n_distinct             | 1000
most_common_vals       | {318,564,596,...}
most_common_freqs      | {0.00173333,0.0017,0.00166667,0.00156667,...}
histogram_bounds       | {0,8,20,30,39,...}
correlation            | 1
most_common_elems      | 
most_common_elem_freqs | 
elem_count_histogram   | 

單個欄位統計信息的不足 這些單列統計信息可幫助優化器(planner)預計篩選條件的選擇性(這是計劃程式用來估計索引掃描將選擇多少行的方法)。 當查詢中提供多個條件時,優化器(planner)將假定列(或where子句條件)彼此獨立。 當列之間相互關聯或存在相互依賴時,情況並非如此,這會使計劃者估算或低估了這些條件將返回的行數。(譯者註:對於相關性列,優化器預估的比實際數據行數要少)
讓我們看下麵的幾個例子。為了使計劃易於閱讀,我們通過將max_parallel_workers_per_gather設置為0來關閉每個查詢的並行性;
EXPLAIN ANALYZE SELECT * FROM tbl where col1 = 1;                            
                                                QUERY PLAN                                                 
-----------------------------------------------------------------------------------------------------------
 Seq Scan on tbl  (cost=0.00..169247.80 rows=9584 width=8) (actual time=0.641..622.851 rows=10000 loops=1)
   Filter: (col1 = 1)
   Rows Removed by Filter: 9990000
 Planning time: 0.051 ms
 Execution time: 623.185 ms
(5 rows)
如您在此處看到的,優化器(planner)估計col1的值為1的行數為9584,查詢返回的實際行數為10000。因此,非常準確。 但是,當您在第1列和第2列中都包含過濾器時,會發生什麼情況。
EXPLAIN ANALYZE SELECT * FROM tbl where col1 = 1 and col2 = 0;                            
                                                QUERY PLAN                                                
----------------------------------------------------------------------------------------------------------
 Seq Scan on tbl  (cost=0.00..194248.69 rows=100 width=8) (actual time=0.640..630.130 rows=10000 loops=1)
   Filter: ((col1 = 1) AND (col2 = 0))
   Rows Removed by Filter: 9990000
 Planning time: 0.072 ms
 Execution time: 630.467 ms
(5 rows)
優化器(planner)的估算已經降低了100倍!讓我們嘗試瞭解為什麼會發生這種情況。 第一列的選擇性約為0.001(1/1000),第二列的選擇性為0.01(1/100),為了計算將被這兩個“獨立”條件過濾的行數,計劃器將其選擇性乘以。 因此,我們得到:選擇性= 0.001 * 0.01 = 0.00001。
將其乘以表中的行數,即10000000,我們得到100。這就是計劃者估計的100的來源。
如果這些列不是獨立的(有多個列之間存在依賴關係),我們如何告訴優化器(planner)呢? 譯者註:
早些年曾經執著地研究過SQLServer對非相關列預估的演算法,
類似於pg,SQLServer從預估行數從2012版的p0*p1*p2*p3……*RowCount,演變為P0*P11/2  * P21/4 * P31/8……* RowCount,https://www.cnblogs.com/wy123/p/5790855.html   PostgreSQL創建統計表信息 在Postgres 10之前,沒有一種簡單的方法可以告訴優化器(planner)收集統計數據,這些統計數據捕獲了列之間的這種關係。 但是,在Postgres 10中,有一個新功能可以解決此問題。  CREATE STATISTICS可用於創建擴展的統計對象,這些對象告訴伺服器收集有關這些有趣的相關列的額外統計信息。

 

相關列的統計信息(Functional dependency statistics)
回到我們先前的估計問題,問題在於col2的值實際上只是col的1/10。 譯者註:一個表中有兩個欄位c1和c2,比如c1代表“省份Id”,c2代表“縣Id”,這樣c1和c2就存在依賴關係。 在資料庫術語中,我們可以說col2在功能上取決於col1。這意味著col1的值足以確定col2的值,並且沒有兩行具有相同的col1值但具有不同的col2值。 因此,col2上的第二個過濾器實際上不會刪除任何行!但是,優化器(planner)可以捕獲足夠的統計信息來瞭解這一點。 我們創建一個統計對象以捕獲有關這些列的功能依賴性統計並運行ANALYZE。
CREATE STATISTICS s1 (dependencies) on col1, col2 from tbl; 
ANALYZE tbl;
讓我們看看planner現在提出了什麼。
EXPLAIN ANALYZE SELECT * FROM tbl where col1 = 1 and col2 = 0;                            
                                                QUERY PLAN                                                 
-----------------------------------------------------------------------------------------------------------
 Seq Scan on tbl  (cost=0.00..194247.76 rows=9584 width=8) (actual time=0.638..629.741 rows=10000 loops=1)
   Filter: ((col1 = 1) AND (col2 = 0))
   Rows Removed by Filter: 9990000
 Planning time: 0.115 ms
 Execution time: 630.076 ms
(5 rows)
好多了!讓我們來看看是什麼幫助優化器(planner)做出了這一決定。
SELECT stxname, stxkeys, stxdependencies                                                  
  FROM pg_statistic_ext                                                                   
  WHERE stxname = 's1';   
stxname | stxkeys |   stxdependencies    
---------+---------+----------------------
 s1      | 1 2     | {"1 => 2": 1.000000}
(1 row)
綜上所述,我們可以看到Postgres意識到col1完全確定col2,因此捕獲該信息的繫數為1。現在,所有在這兩個列上都具有過濾器的查詢將具有更好的估計。   非相關列的統計信息(ndistinct statistics) 功能依賴性是可以在列之間捕獲的一種關係。您可以捕獲的另一種統計數據是一組列的不同值的數量。 前面我們曾提到,計劃者為每一列捕獲了不同值數量的統計信息,但是當組合多個列時,這些統計信息常常是錯誤的 譯者註:比如一個訂單表中有兩個欄位c1和c2,比如c1代表“UserId”,c2代表訂單類型“OrderType”(假如有服飾,食品,3C產品等),很明顯,一個用戶可以隨意購買任何類型的商品,UserId和OrderType之間沒有任何依賴關係 糟糕的統計數據何時會傷害我們?讓我們來看一個例子。
EXPLAIN ANALYZE SELECT col1,col2,count(*) from tbl group by col1, col2;                   
                                                         QUERY PLAN                                                          
-----------------------------------------------------------------------------------------------------------------------------
 GroupAggregate  (cost=1990523.20..2091523.04 rows=100000 width=16) (actual time=2697.246..4470.789 rows=1001 loops=1)
   Group Key: col1, col2
   ->  Sort  (cost=1990523.20..2015523.16 rows=9999984 width=8) (actual time=2695.498..3440.880 rows=10000000 loops=1)
         Sort Key: col1, col2
         Sort Method: external sort  Disk: 176128kB
         ->  Seq Scan on tbl  (cost=0.00..144247.84 rows=9999984 width=8) (actual time=0.008..665.689 rows=10000000 loops=1)
 Planning time: 0.072 ms
 Execution time: 4494.583 ms
彙總行時,Postgres選擇進行哈希彙總或組彙總。如果它適合哈希表在記憶體中,則選擇哈希聚合,否則選擇對所有行進行排序,然後根據col1,col2將它們分組。 現在,優化器(planner)估計的數量(等於col1和col2的不同值的數量)將為100000。 它發現它沒有足夠的work_mem將該哈希表存儲在記憶體中。因此,它使用基於磁碟的排序來運行查詢。 但是,正如您在計劃的實際部分中看到的那樣,實際行數僅為1001。也許,我們有足夠的記憶體來將它們容納在記憶體中,併進行哈希聚合。 讓我們要求優化器(planner)捕獲n_distinct統計信息,然後重新運行查詢並找出答案。
CREATE STATISTICS s2 (ndistinct) on col1, col2 from tbl;                                  
ANALYZE tbl;


EXPLAIN ANALYZE SELECT col1,col2,count(*) from tbl group by col1, col2;                   
                                                      QUERY PLAN                                                       
-----------------------------------------------------------------------------------------------------------------------
 HashAggregate  (cost=219247.63..219257.63 rows=1000 width=16) (actual time=2431.767..2431.928 rows=1001 loops=1)
   Group Key: col1, col2
   ->  Seq Scan on tbl  (cost=0.00..144247.79 rows=9999979 width=8) (actual time=0.008..643.488 rows=10000000 loops=1)
 Planning time: 0.129 ms
 Execution time: 2432.010 ms
(5 rows)
您可以看到估算值現在更加準確(即1000),查詢現在快了2倍。通過運行下麵的查詢,我們可以看到優化器(planner)學到了什麼。
SELECT stxkeys AS k, stxndistinct AS nd                                                   
  FROM pg_statistic_ext                                                                   
  WHERE stxname = 's2'; 
  k  |       nd       
-----+----------------
 1 2 | {"1, 2": 1000}
 Real-world implications

實際情況下的影響
在實際的生產模式中,您總是會擁有某些列,而這些列之間具有資料庫不知道的相互依存關係。我們與Citus客戶一起看到的一些例子是:
  • 由於要在報表中顯示按所有人分組的統計信息,因此具有月,季度和年的列。
  • 地理層次結構之間的關係,例如:具有國家,州和城市列,並按它們進行過濾/分組。
此處的示例在數據集中只有1000萬行,我們已經看到,使用CREATE統計信息可以在有相關列的情況下顯著改善計劃,並且還可以提高性能。 在Citus用例中,我們的客戶存儲著數十億行數據,而不良計劃的後果可能非常嚴重。 在我們的示例中,當計劃者選擇了一個糟糕的計劃時,我們不得不對1000萬行進行基於磁碟的排序,想像一下數十億行會是多麼糟糕。   Postgres越來越好
當我們著手構建Citus時,我們明確選擇了Postgres作為基礎。通過擴展Postgres,我們選擇了一個堅實的基礎,可以隨著每個發行版的不斷完善。
因為Citus是純粹的擴展,而不是分支,所以使用Citus時可以利用每個發行版中的所有出色新功能。   享受您正在閱讀的內容嗎?
如果您有興趣閱讀我們團隊的更多帖子,請註冊我們的每月時事通訊,並將最新內容直接發送到您的收件箱。
     
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • centos7安裝配置jdk1.8 第一步:下載JDK 鏈接:https://pan.baidu.com/s/1sXWzvL9Tv7HIDxDPIw70SQ 提取碼:vpbi 第二步:將JDK8上傳到linux 在/home目錄下新建一個文件夾soft,以後壓縮包都放到這裡 第三步:解壓jdk-8u ...
  • 存儲器的主要功能是存儲程式和各種數據,並能在電腦運行過程中高速、自動地完成程式或數據的存取。存儲器單元實際上是時序邏輯電路的一種。按存儲器的使用類型可分為只讀存儲器(ROM)和隨機存取存儲器(RAM),兩者的功能有較大的區別,因此在描述上也有所不同。存儲的基礎部分分為ROM和RAM。 常見存儲器分 ...
  • ###Windows驅動 // type.h #ifndef TYPE_H #define TYPE_H #include <setupapi.h> struct wdi_device_info { struct wdi_device_info *next; unsigned short vid; ...
  • 這是一款可以定製任意windows 解析度的軟體;但是需要底層顯卡驅動的支持,不管是獨立顯卡還是核顯; 通常使用顯卡管理設備也可以定製解析度;但是用這個套件比較方便一點;我在這裡進行備份,方便我下一次可以找到; 工具主頁:https://www.monitortests.com/forum/Thre ...
  • 總結一點,部署tomcat環境,首先要安裝jdk,因為tomcat本身就是java語言編寫,我們要在伺服器上安裝好java程式運行環境和標準類庫等組件,tomcat才可以伺服器上正常的運行起來;其實從上面的演示我們大概也能知道tomcat就是為Java jsp程式提供一個運行環境,後續的jsp代碼... ...
  • 背景:荔枝派nano 運行 RTT (rt-thread) 。 使用 RTT 提供的 bootload 很複雜,編譯 bin 之後需要打包成 ota 包(圖形界面,無法使用 bat 等方式集成操作),才能下載進板子進行更新。 本文描述的 boot 來自於 https://gitee.com/zhan ...
  • 容器是一種輕量級、可移植、自包含的軟體打包技術,使應用程式可以在幾乎任何地方以相同的方式運行。 ...
  • DQL:查詢表中的記錄 * select * from 表名; 1. 語法: select 欄位列表 from 表名列表 where 條件列表 group by 分組欄位 having 分組之後的條件 order by 排序 limit 分頁限定 2. 基礎查詢 1. 多個欄位的查詢 select ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...