數據庫性能調優之始: analyze統計信息

摘要:本文簡單介紹一下什麼是統計信息、統計信息記錄了什麼、爲什麼要收集統計信息、怎麼收集統計信息以及什麼時候收集統計信息。

1 WHY:爲什麼需要統計信息

1.1 query執行流程

下圖描述了GaussDB的SQL引擎從接收客戶端SQL語句到執行SQL語句需要經歷的關鍵步驟,以及各個流程中可能對執行產生影響的因素

1) 詞法&語法解析

按照約定的SQL語句規則,把輸入的SQL語句從字符串轉化爲格式化結構(Stmt),如果SQL語句存在語法錯誤,都會在這個環節報錯。

2) 語義解析

語義解析類似一個翻譯器,把外部輸入的可視化的對象翻譯爲數據庫內部可識別的對象(比如把Stmt中以字符串記錄的表名稱轉化爲數據庫內部可識別的oid),如果語句存在語義錯誤(比如查詢的表對象不存在),數據庫會在這個環節報錯。

3) 查詢重寫

根據規則將“語義解析”的輸出等價轉化爲執行上更爲優化的結構,比如把查詢語句中的視圖逐層展開至最低層的表查詢。

4) 查詢優化

數據庫確認SQL執行方式、生成執行計劃的過程

5) 查詢執行

根據執行計劃執行SQL並輸出結果的過程

整個執行流程中,優化器決定了查詢語句的具體執行方式,對SQL語句的性能起着關鍵性的作用。數據庫查詢優化器分爲兩類:基於規則的優化器(Rule-Based Optimizer,RBO) 和基於代價的優化器(Cost-Based Optimizer,CBO)。RBO是一種基於規則的優化,對於指定的場景採用指定的執行方式,這種優化模型對數據不敏感;SQL的寫法往往會影響執行計劃,不瞭解RBO的細則的人員開發的SQL性能不可控,因此RBO逐漸被拋棄,目前GaussDB等數據庫廠商的優化器都是CBO模型。CBO模型是根據SQL語句生成一組可能被使用的執行計劃,並估算出每種執行計劃的代價,最終選擇選擇一個代價最小的執行方式。

1.2 CBO模型

數據庫執行SQL語句的時候,會把執行拆分爲若干步驟,如下SQL

select *
from t1 join t2 on t1.a=t2.b
where t1.b = 2 and t2.a = 3;

在具體執行的時候會拆分爲表掃描和表關聯兩個主要查詢動作。這兩個查詢動作都存在多種執行方式,比如表掃描均存在SeqScan、IndexScan、IndexOnlyScan、BitmapScan等多種執行方式、表關聯存在NestLoop、HashJoin、MergeJoin三種執行方式,那麼在具體的業務場景下什麼樣的查詢動作纔是代價最小的執行方式,這就是優化器的核心工作。

CBO主要工作原理是通過代價模型(Cost Model)和統計信息估算每種執行方式的代價,然後選擇一種執行代價最優的執行方式。這裏面代價模型是核心算法邏輯,統計信息是cost計算的數據源,二者配合完成cost計算;如果統計信息缺失,計算時代價模型會使用默認值來計算cost,當然這時cost會跟真實值存在較大偏差,大概率會出現選擇非最優執行計劃的情況,因此統計信息是CBO模型中 cost計算的數據輸入,是CBO最核心的科技之一。

2 WHAT:都有哪些統計信息

統計信息是指數據庫描述表或者索引數據特徵的信息,常見的有表記錄條數、頁面數等描述表規模的信息,以及描述數據分佈特徵的MCV(高頻非NULL值)、HISTOGRAM(直方圖)、CORRELATION等信息。

本文中通過如下用例來展示統計信息是如何表現表的數據特徵的

DROP TABLE public.test;
CREATE TABLE public.test(a int, b int, c int[]);
INSERT INTO public.test VALUES (generate_series(1, 20), generate_series(1, 1200));
INSERT INTO public.test VALUES (generate_series(1, 1200), generate_series(1, 1200));
UPDATE public.test SET c = ('{' || a || ','|| a || '}')::int[] WHERE b <= 1000;
UPDATE public.test SET c = ('{' || a || ','|| b || '}')::int[] WHERE b > 1000;
ANALYZE public.test;

3 WHERE:統計信息在哪裏

3.1 表規模信息

系統表pg_class中的reltuples和relpages兩個字段能夠反映表規模信息信息,其中relpages記錄了表數據存儲到幾個page頁裏面,主要用於表從存儲接口掃描數據的代價計算;reltuples記錄了表記錄條數,主要用於掃描結果集行數估算。

查詢pg_class中的表規模估算信息,顯示錶爲2400行

單表全量數據查詢,通過explain查看錶規模估算,顯示錶掃描輸出行數估算爲2400。

3.2 單列統計信息

單列統計信息是指表的單列的數據特徵信息,存儲在系統表pg_statistic中。因爲pg_statistic會存儲一些關鍵採樣值來描述數據特徵,因此pg_statistic數據是敏感的,只有超級用戶纔可以訪問pg_statistic。通常我們推薦用戶使用查詢系統視圖pg_stats來查詢當前用戶有查詢權限的表的統計信息,同時pg_stats信息的可讀性更強,pg_stats字段信息如下

查詢表public.test的a列的數據特徵信息如下

通過統計新可以看出public.test的a列的NULL值比例爲0,存在120個distinct值, 1~20是MCV值,每個出現的概率是0.0254167;21~1200出現在在直方圖統計信息中;

以查詢語句“SELECT count(1) FROM public.test WHERE a < 44;”爲例說明統計信息在優化過程中行數估算場景下的作用

a) 所有MCV值均滿足a < 44,所有MCV值的比例爲0.0254167 * 20 = 0.5083340

b) 44爲直方圖中第三個邊界,直方圖中滿足a < 44的值的比例爲(1-0.5083340)/100 *(3-1)= .0098333200

那麼表中滿足a<56的tuples的個數爲1243.6015680 ≈1244,通過explain打印執行計劃如下

3.3 擴展統計信息

擴展統計信息存儲在系統表pg_statistic_ext裏面,當前只支持多列統計信息這一種擴展統計信息類型。pg_statistic_ext會存儲一些關鍵採樣值來描述數據特徵,因此pg_statistic_ext數據是敏感的,只有超級用戶纔可以訪問pg_statistic_ext,通常我們推薦用戶使用查詢系統視圖pg_ext_stats來查詢當前用戶有查詢權限的擴展統計信息。

表的多個列有相關性且查詢中有同時基於這些列的過濾條件、關聯條件或者分組操作的時候,可嘗試收集多列統計信息。擴展統計信息需要手動進行收集(具體收集方法,下個小節會介紹),如下爲test表(a,b)兩列的統計信息

4 HOW:如何生成統計信息

4.1 顯式收集統計信息

4.1.1 單列統計信息

通過如下命令收集單列統計信息:

{ ANALYZE | ANALYSE } [ VERBOSE ]  [ table_name [ ( column_name [, ...] ) ] ];

如語法描述,我們支持對指定列做統計信息,但是實際上我們很難統計實際業務SQL中到底使用了當前哪些表的列進行了代價估算,因此建議通常情況下對全表收集統計信息。

4.1.2 擴展統計信息

通過如下命令收集多列統計信息:

{ANALYZE | ANALYSE} [ VERBOSE ] table_name (( column_1_name, column_2_name [, ...] ));

需要注意的是,當前只支持在百分比採樣模式下生成擴展統計信息,因此在收集擴展統計信息之前請確保GUC參數default_statistics_target爲負數

4.2 提升統計信息質量

analyze是按照隨機採樣算法從表上採樣,根據樣本計算表數據特徵。採樣數可以通過配置參數default_statistics_target進行控制,default_statistics_target取值範圍爲-100~10000,默認值爲100。

1) 當default_statistics_target > 0時;採樣的樣本數爲300*default_statistics_target,default_statistics_target取值越大,採樣的樣本也越大,樣本佔用的內存空間也越大,統計信息計算耗時也越長

2) 當default_statistics_target < 0時,採樣的樣本數爲 (default_statistics_target)/100*表的總行數,default_statistics_target取值越小,採樣的樣本也越大。但是default_statistics_target < 0時會把採樣數據下盤,不存在樣本佔用的內存空間的問題,但是因爲樣本過大,計算耗時長的問題同樣存在

default_statistics_target < 0時,實際採樣數是(default_statistics_target)/100*表的總行,所以我們又稱之爲百分比採樣。

4.3 自動收集統計信息

當配置參數autoanalyze打開時,查詢語句走到優化器發現表不存在統計信息,會自動觸發統計信息收集,以滿足優化器的需求。以文檔的case爲列

注:只有對統計信息敏感的複雜查詢動作(多表關聯等操作)的SQL語句執行時纔會觸發自動收集統計信息;簡單查詢(比如單點,單表聚合等) 不會觸發自動收集統計信息

5 WHEN:什麼時候收集統計信息

5.1 大規模數據變化

大規模數據導入/UPDATE/DELETE等操作,會導致表數據行數變化,新增的大量數據也會導致數據特徵發生大的變化,此時需要對錶重新收集統計信息

5.2 查詢新增數據

常見於業務表新增數據查詢場景,這個也是收集業務中最常見、最隱蔽的統計信息沒有及時更新的問題,這種場景最主要的特徵如下

1) 存在一個按照時間增長的業務表

2) 業務表每天入庫新一天的數據

3) 數據入庫之後查詢新增數據進行數據加工分析

在最後步驟的數據加工分析時,最長的方法就是使用Filter條件從分區表中篩選數據,如passtime > ‘2020-01-19 00:00:00’ AND pastime < ‘2020-01-20 00:00:00’,假如新增數據入庫之後沒有做analyze,優化器發現Filter條件中的passtime取值範圍超過了統計信息中記錄的passtime值的上邊界,會把估算滿足passtime > ‘2020-01-19 00:00:00’ AND pastime < ‘2020-01-20 00:00:00’的tuple個數爲1條,導致估算行數驗證失真

6 WHO:誰來收集統計信息

AP場景下業務表數據量一般都很大,單次導入的數據量也比較大,而且經常是數據導入即用,因此建議在業務開發過程中,根據數據變化量和查詢特徵在需要的地方主動對相關表做analyze。

本文分享自華爲雲社區《GaussDB(DWS)性能調優系列基礎篇一:萬物之始analyze統計信息》,原文作者:譡裏個檔。

 

點擊關注,第一時間瞭解華爲雲新鮮技術~

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