作者:王偉(盞一)
1. ADB PG 簡介
AnalyticDB for PostgreSQL是阿裡雲上的MPP資料倉庫服務,其核心采用PostgreSQL引擎,支援标準SQL 2003,相容PostgreSQL/Greenplum,高度相容 Oracle 文法生态;具有存儲計算分離,線上彈性平滑擴容的特點;既支援任意次元線上分析探索,也支援高性能離線資料處理;是面向網際網路、金融、證券、保險、銀行、數字政務、新零售等行業有競争力的資料倉庫方案。

AnalyticDB for PostgreSQL采用MPP架構,執行個體由多個計算節點組成,存儲容量随節點數線性擴充,且保持查詢響應時間不變。
ADB PG 的 CBO 優化器基于表的統計資訊,為查詢選擇最佳的查詢計劃。本次釋出的 Auto Analyze 功能解決了在 ADB PG 執行個體使用過程中,由于未能及時執行 ANALYZE 收集統計資訊導緻了 CBO 優化器生成計劃退化進而導緻業務分析變慢的問題。
2. Analyze 重要性
目前 ADB PG 基于代價的優化器(以下簡稱 CBO),依賴于我們評估一個代價值用于衡量每種候選計劃的代價,而代價的評估又依賴于收集的統計資訊。在我們看來,CBO 和統計資訊之間的關系猶如一把槍和彈藥之間的關系,槍再好如果沒有充足的彈藥的話,那麼無異于巧婦難為無米之炊。統計資訊的收集就是為了給 CBO 提供足夠多合理的資訊,讓 CBO 能夠基于這些統計資訊做出合理的決策。舉個簡單例子,假設我們有表 t 以及 idx_t_z 如下所示:
create table t(i int , j int, z int);
create index idx_t_z on t(z);
insert into t select i, i, i from generate_series(1, 2) i; -- 1
insert into t select i, i, i from generate_series(1, 3333333) i; -- 2
insert into t select i, i, 20181218 from generate_series(1, 10) i;
注:*左右滑動閱覽
這裡第1處 insert 會觸發 ADB PG AutoStats 機制,此時會對表 t 進行一次 ANALYZE,并收集相關統計資訊。接着我們使用 EXPLAN ANALYZE 來執行一條簡單查詢并輸出查詢的執行計劃:
tmp=# explain analyze select * from t where z = 20181218;
Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..2.02 rows=1 width=12) (actual time=287.952..743.833 rows=10 loops=1)
-> Seq Scan on t (cost=0.00..2.02 rows=1 width=12) (actual ti me=287.428..287.430 rows=5 loops=1)
Filter: (z = 20181218)
Planning time: 1.242 ms
(slice0) Executor memory: 59K bytes.
(slice1) Executor memory: 42K bytes avg x 3 workers, 42K byt es max (seg0).
Memory used: 128000kB
Optimizer: Postgres query optimizer
Execution time: 744.675 ms
可以看到,由于在表 t 建立之後,僅在第一次 insert 時觸發了 ANALYZE,在資料更新後未能及時更新統計資訊,使得優化器在優化時看到的統計資訊中記錄着表 t 總行數為 2,使得 CBO 優化器這時錯認為 SeqScan 比 IndexScan 效率更高。但如果我們這裡手動執行下 ANALYZE:
tmp=# ANALYZE t;
ANALYZE
tmp=# explain analyze select * from t where z = 20181218;
Gather Motion 3:1 (slice1; segments: 3) (cost=0.18..8.20 rows=1 width=12) (actual time=0.429..0.439 rows=10 loops=1)
-> Index Scan using idx_t_z on t (cost=0.18..8.20 rows=1 widt h=12) (actual time=0.014..0.016 rows=5 loops=1)
Index Cond: (z = 20181218)
Planning time: 1.305 ms
(slice0) Executor memory: 92K bytes.
(slice1) Executor memory: 60K bytes avg x 3 workers, 60K byt es max (seg0).
Memory used: 128000kB
Optimizer: Postgres query optimizer
Execution time: 1.322 ms
可以看到由于 CBO 使用了更精确的統計資訊,是以其也生成更優的執行計劃,使得查詢執行時間從 700ms 降低到 1ms。
更精準的統計資訊除了能幫助優化器生成更高效的執行計劃之外;也能夠使得 ADB PG 近期釋出的多元排序得到更好的排序效果,排序效果越好,對查詢就有越明顯的加速。
3. AutoStats 介紹
如上所示,正因為 ANALYZE 重要性,為了提升使用者體驗,ADB PG 引入了 AutoStats 機制。AutoStats 機制有如下三種工作模式,受配置 gp_autostats_mode 控制。
- ON_NO_STATS。此時意味着在使用者執行了 Insert/Update/Delete 等 DML 操作之後,ADB PG 會查詢 DML 目标表在 DML 之前的狀态,若目标表在 DML 之前是空表,那麼在 DML 之後,此時 ADB PG 會在同一事務内對目标表觸發一次 ANALYZE 操作。這也是目前 ADBPG 線上預設配置。
- ON_CHANGE。此時意味着在使用者執行了 DML 操作之後,ADB PG 會判斷本次 DML 操作影響到的行數,若影響行數超過一定門檻值,便會對目标表觸發一次 ANALYZE 操作。
- NONE。此時意味着關閉 AutoStats 系統。
可以看到 AutoStats 判斷是否對目标表觸發 ANALYZE 操作隻依據了最近一次 DML 操作的結果,是以 AutoStats 更适合 ETL 業務。但随着 ADB PG HTAP 能力的提升,以及與周邊生态系統鍊路的打通,越來越多的使用者傾向于以流式的方式将資料導入到 ADB PG 中,比如使用阿裡雲資料傳輸服務。這導緻了 AutoStats 越來越乏力。從上面舉的例子也可以看到,AutoStats 隻會在第一次 Insert 之後觸發一次 ANALYZE 操作,使得在第二、三次 insert 之後,表 t 的統計資訊與實際情況完全不符,這也直接導緻了 CBO 未能生成更優的執行計劃。
4. 引入 Auto Analyze
為此 ADB PG 開發了适合場景更廣、對流式插入更友好的 Auto Analyze 系統。Auto Analyze 會為每張表記錄自上次 Analyze 以來所有 Insert/Update/Delete 影響到行數的累加值,之後基于這個累加值,結合表本身大小,來決策是否需要為表執行 Analyze 操作。另外 Auto Analyze 會異步地執行 Analyze 操作,與 AutoStats 在使用者業務事務内同步地執行 Analyze 操作相比,異步 Analyze 執行對使用者業務基本上無感,也不會再有同步執行 Analyze 操作而可能導緻的死鎖等其他問題。而且秉承着擁抱、回饋、融合、回報的開源思想,ADB PG Auto Analyze 也已經貢獻并合入了 Greenplum master 分支。
在開啟 Auto Analyze 的前提下,我們再一次模拟執行文章開頭中舉的例子,如下所示在執行完第3條 insert 之後,Auto Analyze 系統也觸發了對表 t 的一次 ANALYZE 操作。
tmp=# select objid::regclass, staactionname, stasubtype from pg_stat_last_operation where objid = 't'::regclass order by statime desc;
objid | staactionname | stasubtype
-------+---------------+------------
t | ANALYZE | AUTO
t | CREATE | TABLE
(2 rows)
此時使用者不再需要手動執行 ANALYZE,也能使 CBO 生成更優的執行計劃:
Gather Motion 3:1 (slice1; segments: 3) (cost=0.18..8.20 rows=1 width=12) (actual time=0.765..0.773 rows=10 loops=1)
-> Index Scan using idx_t_z on t (cost=0.18..8.20 rows=1 width=12) (actual time=0.013..0.015 rows=5 loops=1)
Index Cond: (z = 20181218)
Planning time: 1.034 ms
(slice0) Executor memory: 92K bytes.
(slice1) Executor memory: 60K bytes avg x 3 workers, 60K bytes max (seg0).
Memory used: 128000kB
Optimizer: Postgres query optimizer
Execution time: 1.647 ms
5. Auto Analyze 實作
5.1 PG Auto Analyze 實作
在介紹 ADB PG Auto Analyze 實作之前,我們首先看下 PostgreSQL 中是如何實作 Auto Analyze 的。PostgreSQL Auto Analyze 的實作依賴着兩個元件:Statistics Collector,Autovacuum。其中 Autovacuum 元件,簡單來說,就是周期性周遊每個庫,對于庫中每個表,根據 Statistics Collector 中這張表相關的統計資訊來判斷是否需要為這張表觸發 Analyze, Vacuum 等操作。Statistics Collector 元件負責收集、儲存、持久化 PG 運作中産生的各種 metric 資訊,如表的增、删、改行數等。Statistics Collector 收集到的所有資訊都儲存在記憶體中。在 Statistics Collector 程序關閉時,會将記憶體中的統計資訊持久化到磁盤檔案中。在 Statistics Collector 程序啟動時,也會從磁盤檔案中讀取之前持久化的統計資訊到記憶體中。Statistics Collector 自身同時也是一個 udp server,監聽着一個特定端口。在 PG 運作中,backend 會在适當時候将自身收集到的 metric 打包成 udp message 發送給 Statistics Collector。下面以 PgStat_StatTabEntry 中資訊的收集為例示範一下此過程:
其中 pgStatTabList 指針指向的結構等同于
LinkedList<PgStat_TableStatus>
,每個 backend 都會将自身收集到表級别的 metric 存放在該數組相應 PgStat_TableStatus 中。
PgStat_TableStatus::trans 指針指向的結構等同于
Vec<PgStat_TableXactStatus>
,其内通過 PgStat_TableXactStatus 存放着目前表在每個事務級别内的統計資訊。PgStat_SubXactStatus 結構存放着一個特定事務級别内所有 PgStat_TableXactStatus 結構,存放着該事務級别内發生的所有統計資訊。pgStatXactStack 總是指向着目前事務級别對應的 PgStat_SubXactStatus 結構。當在某個事務級别内 backend 打開表準備執行增删改時,會從 pgStatTabList 指向的數組中選擇一個 PgStat_TableStatus 元素指派給 RelationData::pgstat_info。之後在執行 IUD(Insert/Update/Delete) 操作時,backend 會更新目前事務級别上特定表對應的 PgStat_TableXactStatus 結構。每當一個子事務 commit/rollback 時都會将本級别事務内所有 PgStat_TableXactStatus 統計資訊合并到父事務中。在頂層事務 commit/rollback 時會将 PgStat_TableXactStatus 的統計資訊合并到 PgStat_TableStatus.t_counts 中。最後每當 backend 進入 idle 狀态(或者退出)時,會将 pgStatTabList 中所有有效的 PgStat_TableStatus 打包成 udp message 發送給 Statistics Collector,之後将 pgStatTabList 中統計資訊清零。
5.2 ADB PG Auto Analyze 實作
是以 ADB PG Auto Analyze 的實作主要便是在 Insert/Update/Delete 執行結束之後,收集各個計算節點傳回的 Insert/Update/Delete 在各自節點内影響到的行數,之後累加得到 Insert/Update/Delete 影響到的總行數,之後将這個資訊按照 PG Statistics Collector 中做法記錄在對應的 PgStat_TableXactStatus 結構中。最後會在适當時候發送給 ADB PG master 節點的 Statistics Collector 程序。具體細節感興趣的同學可以參考我們将 ADB PG Auto Analyze 貢獻給社群時提的 Pull Request(
https://github.com/greenplum-db/gpdb/pull/10515)。
6. 未來展望
Auto Analyze 的引入使得使用者在使用 ADB PG 執行個體的過程中,統計資訊總是能被及時地收集。這也使得使用者的業務分析将總是能得到較優的執行計劃,業務分析 SQL 的執行性能将不再會由于統計資訊過時而急劇下降。另外在 Auto Analyze 搭建的基礎設施上,結合着線上使用者在使用 ADB PG 中遇到的一些問題,我們也有了接下來的目标:實作 Auto Vacuum 功能。與 Analyze 操作一樣,Vacuum 在 ADB PG 也扮演着重要的角色,相信 Auto Vacuum 的引入也會進一步加強 ADB PG 的使用者體驗。有需要的同學可以掃碼進入釘釘群“雲原生資料倉庫AnalyticDB PostgreSQL版交流群”線上溝通。