物化視圖和視圖的最大區別_基于catalyst的物化視圖改寫引擎的實現

更新日志:
1. 2020/06/16 group by 視圖的部分描述錯誤,已修正。

什么是物化視圖

我先用我的話解釋一下什么是物化視圖。假設我們已經有A,B兩張表,現在我創建了一張表C,

C是由A,B兩張表經過一條SQL處理得到的,這個時候我們就可以認為C是A,B的物化視圖了。那怎么用呢?當一個用戶寫了一條使用A Join B表的SQL,系統會自動嘗試能否改寫成基于C表的查詢,如果成功,那么可能查詢速度就非常快了,因為避免了Join的發生,只是簡單的基于C做了下過濾,但得到的結果和直接使用A,B 是一樣的。

顯然,物化視圖有個很大的問題,就是更新問題,譬如A,B發生了變化,如何保證C 也得到更新。所以這里除了改寫以外,還涉及到了C的創建,管理和更新問題。

現在讓我們引入點術語了,前面我們提到的自動將基于A,B的查詢改寫成基于C的查詢,我們叫Query Rewrite。Query Rewrite 就是將原有的查詢不需要修改,引擎自動選擇合適的物化視圖進行查詢重寫,完全對應用透明。

物化視圖和傳統視圖的最大的區別是,物化視圖存儲不僅存儲了計算邏輯,還存儲了計算結果,并且更進一步的是,作為用戶你無需顯示使用物化視圖,系統會通過Query Rewrite自己來完成內部的改寫。不過無論技術上多先進,我們最后都可以歸納到以空間換時間里去。

SQL Booster

今天我們探討的重點是如何實現Query Rewrite。我去年寫了一個Query Rewrite 引擎[s

ql-booster](https://github.com/aistack/sql-booster),其實是受到阿里李呈祥團隊的relational cache啟發。當時看了他們的分享覺得太棒了,很想立馬就用,但是想著等他們推到開源項目里就太漫長了,加之目前大數據里的物化視圖的實現,已經開源的貌似只有hive了,是基于Calcite實現的,而Spark 的話是自己開發的catlyst引擎,而我自己又重度使用Spark,所以干脆自己動手基于catalyst實現一個。后面在開發過程中也遇到了不少公司也在做類似的實現,也有問我的,可惜一直沒有寫文章,這次趁著周末,寫了,既可以做為交流用,也可以作為備忘錄。因為時間久了,代碼和思路就很容易遺忘,文章可以很容易喚醒記憶,一箭雙雕。

知識準備篇

一個物化視圖由兩部分構成:

1. 生成該物化視圖的SQL

2. 表數據

表數據很簡單,就是為了查詢的。記錄生成該物化視圖的SQL的原因是,我們需要知道這個物化視圖的數據是來源哪些表的,每個字段的是來源哪些表,不然沒辦法做改寫。

Query Rewrite的基本步驟如下

1. 注冊各個視圖,這些視圖都會以AST(Catalyst里的LogicalPlan)存在

2. 待改寫的用戶SQL,這些SQL不會顯示使用物化視圖。

3. 將SQL解析成方便遍歷處理的AST,也是Catalyst里的LogicalPlan,并且經過Analyzed的,因為我們需要明確知道每個字段屬于哪個表。

4. 處理待改寫的LogicalPlan,然后去和每個已經存在的視圖LogicalPlan匹配,對于匹配上的,則實行改寫

5. 最后將該寫過的LogicalPlan重新生成SQL或者直接執行得到結果。

所以實際上,上面涉及到如下幾個概念,大家需要有個基本感知:

1. 所有視圖的LogicalPlan

2. 待改寫的查詢LogicalPlan

Query Rewrite 的分而治之

在思考Query Rewrite實現的的時候,我想到的第一個問題就是,一條待改寫的SQL是不是可能會使用到多個視圖?

答案是肯定的。理由有三:

1. 如果一條SQL只匹配一個視圖,如果該視圖能覆蓋到這條SQL的大部分表,那么該視圖的通用性必然不好。如果只能覆蓋SQL里的一小部分,那么如果只匹配到一個視圖,那么可能最后查詢速度的提升不會太好。

2. 匹配度太低,還會導致大量存儲的使用,否則就相當于不起作用了。

3. 對于一條復雜的SQL,里面會包含各種子查詢,所以作為一個整體的SQL去匹配一個視圖,實現上也是有難度的。

實際上,一條SQL,其復雜度主要來源于子查詢和join。 join是我們需要盡量通過物化視圖消解掉的,而子查詢,本質上就是SQL內置的虛擬視圖,我們希望盡可能通過物化視圖來替換掉這些虛擬視圖(虛擬視圖意味著大量的計算,因為虛擬視圖里一般也會有復雜的Join查詢)。這樣,事情就變得簡單了,我們只要把一條SQL里的所有子查詢都拎出來,最后每個句子都會符合SPJG形式。

所謂SPJG形式的語句是指僅包含如下語法塊的語句:

1. project

2. agg

3. filter

4. join

其他如limit,Order by之類的并不影響視圖替換,我們無需考慮。

如果把子查詢都拉出來,最后會形成一個子查詢樹狀結構,理論上我們只要對葉子節點做處理即可(只包含基礎表的SPJG語句),每個葉子節點一定是符合SPEG格式要求的。當然了,如果我們的物化視圖還帶有層級結構,也就是基于物化視圖上再生成新的物化視圖,那么還可以進一步按現在的邏輯匹配。不過我們先不搞他。我們先只處理非視圖表替換成視圖表的情況。

有了上面的思路,事情就簡單了,因為我們是對很簡單的SQL語句做視圖替換匹配,而且因為一個復雜的SQL會包含很多只包含了基礎表的SPJG語句,我們一一嘗試用物化視圖替換他們就好。

具體做法是,我們把SQL先用Catalyst解析成 Analyzed LogicalPlan,另外我們還要做一些適當的優化,我目前是做了EliminateOuterJoin,PushPredicateThroughJoin,這樣可以將多種原先形式不同的 LogicalPlan 轉化成相同的形式,可以提高命中率。得到了這個語法樹后我們通過AST提供的transformDown/transformUp拿到所有符合SPEG形態的語句。接著拿著這些SPEG語句一一去匹配是不是有符合的物化視圖。

接下來我們在具體看SEPG具體匹配和修改邏輯的之前,我們還需要解決一個問題,我們可能有幾十個甚至上千個物化視圖,一一去匹配效率肯定是不行的,如果快速縮小范圍呢?

一個簡單的視圖倒排索引

我們在創建物化視圖的時候,系統會自動拿到視圖里的主表,也就是join最左側的表。如果該主表被多個視圖包含,最終會形成下面的結構:

主表 -> 視圖1, 視圖2,視圖3... 

注意,這里的主表和視圖,都是Catalyst里的LogicalPlan。

當我們在處理SPEG 語句的時候,我們也按相同的方式拿到主表,然后以它為key去拿到對應的視圖,這個過程是非常快的。得到視圖后,我們會遍歷這些視圖,去看這些視圖里的表是不是和SPEG里出現的表是一樣的,如果是一樣,就算匹配上了。完全匹配上了的視圖,可能也會有多個,然后我們會進一步做測試他們的等價性,如果只有一個匹配上,那萬事大吉,做改寫就好,如果還有多個匹配上,那么可能就需有個打分模型了,不過我們也可以簡單的取第一個匹配上的就完事。

當然了,如果你不怕空間浪費,也可以將每個視圖涉及到的表都拿出來做形成前面的結構,性能上應該會更好,但是內存可能消耗會大一點,這個就要考實現者自己權衡了。

如何將SPEG使用物化視圖進行改寫

改寫其實是要經歷兩個階段的,第一個是匹配階段,第二個才是改寫階段。

因為SPEG組成已經比較簡單了,因為只包含了project/agg/filter/group/join 等幾個部分。所以我們匹配和改寫主要就是針對這么幾個部分。這意味著我們至少需要五個匹配器,五個改寫器。然后執行邏輯是,五個匹配器都去匹配,只有都符合了,才會觸發五個改寫器進行改寫

下面是sql-booster的匹配器和改寫器。

val pipeline = buildPipeline(rewriteContext: RewriteContext, Seq(//filter條件子句的matcher/rewritenew PredicateMatcher(rewriteContext),new SPGJPredicateRewrite(rewriteContext),//groupby 條件子句的matcher/rewritenew GroupByMatcher(rewriteContext),new GroupByRewrite(rewriteContext),//聚合子句的matcher/rewrite new AggMatcher(rewriteContext),new AggRewrite(rewriteContext),//join子句的matcher/rewritenew JoinMatcher(rewriteContext),new JoinRewrite(rewriteContext),//select子句的matcher/rewritenew ProjectMatcher(rewriteContext),new ProjectRewrite(rewriteContext)))

每個匹配器都需要實現一定的規則。比如where條件子句要求視圖的過濾子句必須包含查詢SQL的。什么意思呢?比如假設我們有基礎表A,用戶的原生查詢如下:

select A.a from A where A.a<10; 

物化視圖C的定義是:

select a from A where a<11; 

顯然,在filter(where)里,C的數據集是包含用戶原生查詢的,所以對于where條件我們除了替換成C的a屬性以外,其他的都不用動。

select a from C where a<10; 

實際上,對于這一個簡單的語句,我們至少需要檢查如下兩點:

1. 用戶的project屬性是不是都在 C的project屬性(select語句里的屬性)里

2. 用戶的filter的過濾范圍是不是都在C的filter過濾方位內。

對于join語句,因為前面我們使用了優化器,會將Join On后面的條件語句進行合理下推,在LogicalPlan里會轉化成filter,所以我們只要判斷Join語法書是不是一致即可。

對于帶有agg/group 的則比較復雜。

比如視圖C由如下SQL得到:

select m,c,count(*) as a from A group by m,c

用戶查詢語句如下:

select c,count(*) as a from A group by c  

這個時候匹配比較容易,我們需group by語句的條件視圖是用戶查詢語句的超集,并且順序必須是后面的。比如視圖里是group by m,c 那用戶的查詢只能是m,c 或者c,同時依然要符合視圖的project屬性要包含用戶的所有的project屬性,但是改寫上會麻煩一些,需要改寫成如下形式才會是等價的:

select sum(a) from C group by c  

另外一個例子是avg,他最后要改寫成sum(k)/a(a等于視圖里的avg(k))。具體的一些改寫規則我在文章中就不一一羅列,大家感興趣可以去看看我上面羅列的五個改寫器。

注意: 名詞filter/predicate 是等價的,一般是指過濾條件;project 是指select語句部分;

如何從LogicalPlan轉化會SQL

我其實我希望sql-booster是一個標準的Query Rewrite服務。只要把表和視圖的定義注冊進來,給定一條SQL,就能返回一條改寫后的SQL。所以如何把LogicalPlan轉換回SQL也是一個比較重要的工作。正如我們前面討論的,無論SQL多復雜,最后都是由SPEG的樹狀結構構成,所以我們還原的語句其實會比較簡單,核心就是遞歸處理子查詢,把每個子查詢都轉化成一個標準的SPEG語句。

比較繁瑣的是表達式需要還原回字符串,這個需要大量的枚舉。具體參看org.apache.spark.sql.catalyst.sqlgenerator.LogicalPlanSQL,該代碼主要修改自Moonbox項目,對此表示感謝。

當然了,很多情況我們可能也不需要這個步驟,僅僅需要直接執行改寫后的LogicalPlan或者序列化LogicalPlan后直接發回執行即可。

最后的結束語

物化視圖的Query Rewrite是個需要積累的活,目前sql-booster僅僅是實現了部分匹配/改寫規則,畢竟當時自己好像只開發一到兩個禮拜。不過后續我有時間會繼續完善,也希望能夠在公司應用起來。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/446109.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/446109.shtml
英文地址,請注明出處:http://en.pswp.cn/news/446109.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux加密框架中的算法和算法模式

參考鏈接 Linux加密框架中的算法和算法模式&#xff08;三&#xff09;_家有一希的博客-CSDN博客 對稱算法 14 如上所示&#xff0c;在arc4.c中定義了兩個與RC4算法相關的算法實現&#xff0c;分別為arc4和ecb(arc4)&#xff0c;其中arc4是RC算法的算法實現&#xff0c;而ecb…

python學籍管理系統 flask_taskday05-Python之flask學習 web開發最基本的需要(特別詳細且適用)...

1.首先一個Flask的Web項目的創建需求一(文章概述)&#xff1a;一&#xff1a;必須實現命令工具管理App&#xff0c;用于在命令行輸入命令對項目進行管理&#xff0c;對后期多多益善二&#xff1a;必須實現“藍圖”管理&#xff0c;用于將app啟動函數與路由分開管理&#xff0c;…

Linux加密框架crypto AES代碼相關

例子 aes_generic.c - crypto/aes_generic.c - Linux source code (v5.15.11) - Bootlin static struct crypto_alg aes_alg {.cra_name "aes",.cra_driver_name "aes-generic",.cra_priority 100,.cra_flags CRYPTO_ALG_TYPE_CIPHER,.cra_blocks…

python語言print函數_Python 的 print 函數

Python 2.x 系列已經停止維護了&#xff0c; python 3.x 系列正在成為主流&#xff0c;盡管有些項目還是python2.x 的&#xff0c;之后寫Python 代碼為了保持兼容性&#xff0c;還是盡量和Python 3 標準保持一致作為一個Python newbee 而言&#xff0c; python 2.x 和 3.x 的 …

Linux加密框架crypto crypto_alg|cipher_alg數據結構|AES例子

加密框架將算法的屬性抽象為算法說明數據結構struct crypto_alg&#xff0c;加密框架中的每一個算法&#xff08;基礎算法和衍生算法&#xff09;都表示為一個算法說明數據結構的實例&#xff0c;因此將struct crypto_alg稱為通用算法說明數據結構。后續章節中如無特殊說明&…

python如何運用ols_使用OLS回歸(Python,StatsModels,Pandas)預測未來值

我目前正試圖在Python中實現一個MLR&#xff0c;我不知道如何去應用我發現的未來值的系數。使用OLS回歸(Python&#xff0c;StatsModels&#xff0c;Pandas)預測未來值import pandas as pdimport statsmodels.formula.api as smimport statsmodels.api as sm2TV [230.1, 44.5,…

Linux加密框架 crypto RC4

參考鏈接 arc4.h Linux加密框架中的主要數據結構&#xff08;一&#xff09;_家有一希的博客-CSDN博客 頭文件 arc4.h - include/crypto/arc4.h - Linux source code (v5.15.11) - Bootlin實現代碼 arc4.c arc4.c - crypto/arc4.c - Linux source code (v5.15.11) - Bootlin…

python讀txt轉array_python將txt文件讀入為np.array的方法

原文件&#xff1a;7.8094,1.0804,5.7632,0.012269,0.008994,-0.003469,-0.79279,-0.064686,0.11635,0.68827,5.7169,7.9329,0.010264,0.003557,-0.011691,-0.57559,-0.56121,原文件數據比較多&#xff0c;是一個125行&#xff0c;45類float數字。代碼&#xff1a;# -*- coding…

Linux加密框架 crypto 哈希算法說明 同步哈希shash_alg | 異步哈希 ahash_alg | 通用部分抽象 hash_alg_common

參考鏈接 Linux加密框架中的主要數據結構&#xff08;二&#xff09;_家有一希的博客-CSDN博客 定義 通用算法說明數據結構crypto_alg的聯合體成員變量cra_u中包含多種算法的個性化屬性&#xff0c;如分組算法、塊加密算法、壓縮算法、偽隨機數算法等&#xff0c;但不包含哈希…

python 列表間隔取值_python list數據等間隔抽取并新建list存儲的例子

原始數據如下&#xff1a;[e3cd, e547, e63d, 0ffd, e39b, e539, e5be, 0dd2, e3d6, e52e, e5f8, 0000, e404, e52b, e63d, 0312, e38b]將其分割為4路數據&#xff0c;分別存儲在fetal1、fetal2、mother1、ECG的列表中&#xff0c;各列表對齊&#xff0c;不能整除于4的數據舍去…

Linux加密框架 crypto 哈希算法舉例 MD5

參考鏈接 Linux加密框架 crypto 哈希算法說明 同步哈希shash_alg | 異步哈希 ahash_alg | 通用部分抽象 hash_alg_common_CHYabc123456hh的博客-CSDN博客Linux加密框架中的主要數據結構&#xff08;二&#xff09;_家有一希的博客-CSDN博客 MD5 md5.h - include/crypto/md5.h …

事務沒提交的數據查的出來嗎?_“金三銀四”面試官:說說事務的ACID,什么是臟讀、幻讀?...

一、事務事務是數據庫管理系統執行過程中的一個邏輯單位&#xff0c;由一個有限的數據庫操作序列構成。--摘自百科在MySQL里&#xff0c;事務是在引擎層面實現&#xff0c;比如MyIsam不支持&#xff0c;InnoDB支持面試清單&#xff08;Java崗&#xff09;&#xff1a;JavaJVM數…

Linux加密框架 crypto 算法模板

參考鏈接 Linux加密框架中的主要數據結構&#xff08;三&#xff09;_家有一希的博客-CSDN博客algapi.h - include/crypto/algapi.h - Linux source code (v5.15.11) - Bootlin 定義 struct crypto_template {struct list_head list;struct hlist_head instances;struct modu…

python找最長的字符串_為Python找到最長重復字符串的有效方法(從Pearls編程)

我的解決方案是基于后綴數組。它是由最長公共前綴的兩倍前綴構成的。最壞情況下的復雜度是O(n(logn)^2)。任務”伊利亞特.mb.txt“在我的筆記本上花了4秒鐘。代碼在函數suffix_array和longest_common_substring中有很好的文檔記錄。后一個函數很短&#xff0c;可以很容易地修改…

Linux加密框架 crypto 算法模板 CBC模板舉例

參考鏈接 Linux加密框架中的主要數據結構&#xff08;三&#xff09;_家有一希的博客-CSDN博客https://blog.csdn.net/CHYabc123456hh/article/details/122194754 CBC算法模板 cbc.c - crypto/cbc.c - Linux source code (v5.15.11) - BootlinCBC算法模板屬性 1)CBC算法模板名…

leetcode數組匯總_LeetCode刷題實戰43:字符串相乘

算法的重要性&#xff0c;我就不多說了吧&#xff0c;想去大廠&#xff0c;就必須要經過基礎知識和業務邏輯面試算法面試。所以&#xff0c;為了提高大家的算法能力&#xff0c;這個公眾號后續每天帶大家做一道算法題&#xff0c;題目就從LeetCode上面選 &#xff01;今天和大家…

Linux加密框架 crypto 算法模板 HMAC模板舉例

參考鏈接 Linux加密框架中的主要數據結構&#xff08;三&#xff09;_家有一希的博客-CSDN博客Linux加密框架 crypto 算法模板_CHYabc123456hh的博客-CSDN博客 HMAC算法模板 hmac.c - crypto/hmac.c - Linux source code (v5.15.11) - Bootlinhmac.c - crypto/hmac.c - Linux…

判斷非負整數是否是3的倍數_五年級數學因數與倍數知識點匯總與解題方法技巧...

在日常教學過程中&#xff0c;我發現孩子們和某些家長對學習數學的方法有一些誤區&#xff0c;就是覺著數學&#xff0c;單純就是邏輯思維&#xff0c;只要多做練習題就能學好&#xff0c;但是不是這樣的&#xff0c;低年級的學生&#xff0c;學習數學還是以背誦為主&#xff0…

tcp通訊一次最多能發送多少數據?_關于TCP/IP,必須知道的十個知識點

本文整理了一些TCP/IP協議簇中需要必知必會的十大問題&#xff0c;既是面試高頻問題&#xff0c;又是程序員必備基礎素養。一、TCP/IP模型TCP/IP協議模型&#xff08;Transmission Control Protocol/Internet Protocol&#xff09;&#xff0c;包含了一系列構成互聯網基礎的網絡…

Linux內核crypto子系統的調用邏輯

testmgr.c - crypto/testmgr.c - Linux source code (v5.15.11) - Bootlin上述代碼是內核內部即crypto子系統對外提供密碼服務的測試程序調用流程&#xff1a;crypto API <—> crypto core <—> crypto_register_alg處于用戶態的程序想要調用處于內核態的密碼算法&…