理論背景
早先的系統里提及的計算下推一般指謂詞下推(Predicate pushdown)。最早提及暫不可考,但理論源自於關系代數理論。故90~10年前階段的研究或實現基本都是只提及Predicate Pushdown,這種影響一直影響到了201x年開源盛行:
- Hive和SparkSQL等主流的數倉計算引擎僅提供了PredicatePushdown的接口
- MySQL截止到2020年8.0.x版本也只有通用支持不完善的cond_push和ICP
- postgres_fdw插件開始支持將聚合推到遠端存儲去完成,但不關心遠端存儲的實現
謂詞下推是符合當前關系型數據庫的經典火山模型的一個優化,對於一個帶有通用目標的數倉引擎/DB計算引擎,火山模型(或帶塊優化的火山模型)依然是框架實現的首選。
但以Oracle為代表的商業數據庫一體化解決方案以及雲數據庫的崛起指出了另一條道路:定制化計算型存儲。計算下推的涵蓋范圍由此從基本的謂詞+投影下推延伸到了數據庫所支持的一切可能計算的下推。
計算下推目標問題
Resource reduction (IO/CPU)
- Case 1: total reduction
- Case 2: tradeoff. Maybe CPU reduces, IO increases.
- Decompression offload : (IO snappy + CPU decompress) VS. (CSS decompress + IO raw)
Performance improvement
- usually brought by resource reduction (especially network IO)
謂詞下推
P : X→ {true, false} called a predicate on X .
Sargable (Search argument able) Sargable是相對具體實現而言的,通俗可以理解為某個謂詞在當前數據庫引擎實現里是Sargable的話,那么他就是可以下推優化的。一般情況下,僅where predicate 可下推,join predicate不能下推。主要是join關系一般沒辦法下推,除非在一些特殊案例下。
在一些Optimizer優化中,我們還會見到上推/上提的概念,通常是子查詢的上提優化的一些變種說法。這里需區分我們所說的計算下推指的是將計算下推到存儲層,是執行器層面的問題,而不是優化器邏輯計划中的不同query block的predicates move-around。其中join predicate pushdown通常就是這類問題之一。JPPD包括如下優化思路:
- JOIN 左表的join 列(一般是數值型索引列)有足夠的統計信息可以支撐優化。 (see SparkSQL)
- 將外表的帶索引join列信息通過join predicate推入視圖子查詢。 (see Oracle JPPD)
投影下推
投影下推的IO減少與存儲方式有着強相關關系,一般作用於行存儲中,因為列存儲天生帶了投影,所以再下推一般意義不大。(但對某些行列存儲場景可能適用)
根據相關論文測試結果,在HDD,在投影列大小占行大小的約85%以上時,列存儲的查詢性能會不如行存儲。因為此時列存儲需要打開更多的磁盤文件,每一次切換都帶來一次磁盤尋道開銷,而行存儲則享受完全的順序掃描。但在現如今的雲數據庫基本都是使用NVMe SSD,新的結果有待測試。
JOIN操作下推
一般在一些Nosql里如下存儲形式的數據比較適合JOIN操作下推:
Join field partitionable:
- parent-child in Object format storage (document storage)
- join predicates on partition key (shards or partition storage)
Broadcast Join:
- Load small table into a filter (Hash/BloomFilter) and push it down
此處概念需區別於join predicate pushdown (可能某些資料也會縮寫成join pushdown) 。(見謂詞下推)
聚合下推
行列下推作用的只是一行數據或者關聯的幾個block的數據,而聚合下推作用的則是整個table的數據(不考慮表連接的情況下),因此通常聚合查詢下推的做法不再僅僅是把查詢條件下放,而會額外增加一些存儲層索引或預計算等。涉及到的索引有如下幾種分類方法:
-
強索引: 對用戶所需的字段查詢建立了直接的索引
-
弱索引: 僅對部分列(通常為主鍵或分區鍵)建立索引,或只建立分區/shard級別的部分索引
-
全局索引:存儲層有獨立的全局索引機制如支持聚合計算
-
局部索引:每個分區維護自己的索引,查詢時類似map-reduce,先在局部查詢,然后在引擎聚合再次計算最終結果
-
StorageIndex: 來自於Oracle的概念,狹義指存儲單位級別的一些弱索引,比如block級別的max/min/count等
-
持久化索引:索引是固化到磁盤的。(大部分的索引形態)
-
內存索引: 索引是臨時存在於內存的,比如一些內存形態的臨時索引優化
最后需注意:對於一些多層計算的架構(比如AWS Redshift里的計算下推),把部分計算從上一層放到下一層(但還是計算引擎),也都是計算下推的范疇。但這類下推相對於下推到存儲來說,挑戰是不一樣的。
同時,更廣義計算下推不再限制於SQL計算下推,比如LSM數據庫的compaction offloading等,廣義下推概念在雲數據庫領域上和數據庫存儲融合概念兩者邊界逐漸模糊。
