一文詳解物化視圖改寫

預計算和緩存是計算機領域提高性能以及降低成本的最常見的手段之一。對於那些經常重複的請求,如果可以通過緩存回答,比重新計算結果或從速度較慢的數據存儲中讀取要快得多,消耗更少的系統資源。在數據庫領域中,物化視圖是預計算和緩存的自然體現。

本文主要介紹什麼是物化視圖,以及如何實現基於物化視圖的查詢改寫。

在第一部分,我們會簡單介紹物化視圖,並介紹基於物化視圖的查詢改寫的用途。在第二部分,我們將介紹查詢優化器使用物化視圖進行查詢改寫的匹配和改寫過程。最後,我們將介紹查詢改寫的幾種實現方式,及其優缺點。

背景介紹

物化視圖

物化視圖是將查詢結果預先計算並存儲的一張特殊的表。"物化"(Materialized) 這個詞是相對於普通視圖而言。普通視圖較普通的表提供了易用性和靈活性,但無法加快數據訪問的速度。物化視圖像是視圖的緩存,它不是在運行時構建和計算數據集,而是在創建的時候預先計算、存儲和優化數據訪問,並自動刷新來保證數據的實時性。

對於數據倉庫,物化視圖最重要的功能就是查詢加速。數據倉庫中存在大量在大型表上執行復雜的查詢,這些查詢會消耗大量資源和時間。物化視圖可以通過預計算的結果回答查詢,消除昂貴的聯接和聚合所帶來的開銷,大幅度改善查詢處理時間,降低系統負載。對於可以預見並反覆使用相同子查詢結果的查詢,物化視圖特別有用。

爲了實現物化視圖的潛力,需要解決三個問題:

  1. 物化視圖選擇:選擇哪些查詢和表構建物化視圖;
  2. 物化視圖維護:減少物化視圖更新成本和時間;
  3. 物化視圖運用:如何使用物化視圖加速查詢。

本文主要從查詢優化器的角度,介紹使用物化視圖加速查詢背後的技術實現。

基於物化視圖的查詢改寫

直接查詢物化視圖可以大幅度改善查詢處理時間,但是需要用戶修改查詢語句。使用物化視圖加速查詢的一個重要問題是,如何採用一種系統化和自動化的方法,自動使用物化視圖回答查詢。通過這種透明改寫,物化視圖可以像索引一樣添加或刪除,而不會影響已有 SQL。

查詢改寫使得物化視圖具有廣泛的用途:

  1. 物化視圖可以透明地改寫查詢,無需改造業務就能使用物化加速查詢;
  2. 方便地應用緩存公共結果集,以及預計算等跨查詢優化手段;
  3. 對於數據倉庫,數據集成場景,物化視圖可以物化外表結果,屏蔽多個數據源的差異,實現本地副本或讀寫分離;
  4. 查詢改寫結合自動構建物化視圖,實現數據庫自治加速。

查詢改寫的問題定義

爲了實現更大範圍的改寫,查詢改寫通常被集成在優化器規則中。這有幾個方面的好處。

首先查詢改寫可以利用優化器其他規則。依靠優化器其他規則將查詢轉換成標準和統一的形式,簡化匹配流程,增加改寫範圍。其中比較重要的規則是列消除,謂詞下推,解關聯子查詢等。解關聯子查詢規則允許物化視圖對包含關聯子查詢的查詢進行改寫加速。

其次,優化器可以遞歸每一個子樹能否被某個視圖進行改寫。每個相關視圖都會對每個子樹產生多次改寫,一個查詢語句不同部分可能被不同的視圖改寫。最終所有的改寫都進入基於成本的選擇器中,與原始查詢一起選出最優的查詢計劃。

查詢改寫算法只需要考慮給定的查詢表達式和視圖,判斷這個查詢表達式能否從視圖中計算出來,然後從視圖上構造一個等價的補償表達式,與原查詢表達式等價。查詢改寫的範圍應該儘可能大,查詢改寫的目標是使用少量物化視圖改寫大量查詢。最終由優化器選擇出一個最優的查詢計劃。

查詢改寫檢查

優化器通過多種方式來改寫查詢。最簡單的一種情況是物化視圖的查詢與查詢完全匹配,符合這種查詢重寫類型的查詢數量很少。爲了進行更通用的匹配,優化器會嘗試使用各種規則構造一個等價表達式改寫查詢。

查詢改寫檢查包含兩個步驟,改寫匹配檢查和構建等價表達式。一個查詢能被視圖回答需要滿足下面兩個條件:

  1. 物化視圖 Join 關係在查詢中存在
  2. 物化視圖有足夠的數據來回答查詢

部分條件下即使不滿足條件,視圖也可能會被用於改寫。例如視圖包含一部分查詢所需要的數據,可以使用物化視圖回答部分查詢,剩下的數據從原始數據中計算。這部分改寫檢查會放在高級改寫規則中進行介紹。

具體來說改寫檢查會依次進行 Join 檢查和 Ouput 檢查,如果查詢或視圖中含有 Grouping By 和 Aggregation,還會進行額外的檢查,如果需要,會嘗試對視圖進一步聚合。

Join 檢查

當查詢和視圖的表的 Join 關係相同時,視圖纔可能包含查詢需要的所有的行和列。一種簡單的方式是隻考慮沒有子查詢的 Inner Join,這時只用比較查詢表和數量是否完全一致即可。或者通過一系列規則檢查查詢和視圖的關係代數樹包含的 Join 關係是等價的。更通用的一種方法是構建 Join Graph。Join Grpah 是一個以關係爲結點,聯接爲邊的圖。Inner Join 的條件表示爲無向邊,Outer Join 的條件表示爲有向邊。Join Graph 由查詢關係代數樹構建而來,通過比較 Join Graph 就可以檢查物化視圖和查詢包含相同的 Join 關係。

例如對於下面的查詢

select c_custkey, c_name, l_orderkey, l_partkey, l_quantity
from (
  select l_orderkey,l_partkey, l_quantity 
  from lineitem 
  where l_orderkey < 1000
  and l_shipdate = l_commitdate
) subquery
join orders on subquery.l_orderkey = o_orderkey
left join customer on o_custkey = c_custkey

對於物化視圖

create materialized view mview1
enable query rewrite as
select *
from lineitem 
join customer On subquery.l_orderkey = o_orderkey
left outer join orders On o_custkey = c_custkey

查詢和物化視圖具有相同的 Join 關係,查詢可以被改寫

select c_custkey, c_name, l_orderkey,l_partkey, l_quantity
from mview1
where l_orderkey < 1000
and l_shipdate = l_commitdate

Output 檢查

Ouput 檢查保證物化視圖有足夠的數據回答查詢,這包括 3 個步驟:

  1. 驗證查詢所有輸出需要的列能夠從視圖中計算出來
  2. 優化器需要檢查視圖包含查詢所有需要的行數,也就是視圖的謂詞的範圍大於查詢的範圍
  3. 補償謂詞能否從視圖中計算得來

等價關係

查詢改寫能夠通過查詢中的等價關係擴展改寫的範圍。因爲等值條件具有傳遞性,等價關係可以從等值條件或者代數關係推導而來。例如可以從 A=date_format(now(), '%Y%m%D') 和 B=date_format(now(), '%Y%m%D'),因爲函數是確定性的,可以得到 A=B,如果我們還有 inner join 的條件 B=C,可以進一步得到 A=B=C。

可能會有某些其他的條件可以推導出等價關係,例如可以從 A=2,B=3,C=5 推導出 C = A + B。相比等值關係,尋找這種關係會使搜索過程更加複雜,而且我們無法實現所有的可能的相等關係,因此很少考慮這樣的表達式。等價關係推導只搜索等值條件,即使可能錯過一些改寫機會,這樣的淺層搜索可以保證速度。

表達式檢查

爲了確保查詢輸出的列和補償謂詞需要的列都能從視圖中計算出來,我們需要一種方法確定來自查詢的表達式是否和視圖中的表達式相等,或者能從中計算出來。表達式檢查無法純粹從語法上實現,兩個表達式或者符號的文本相同,並不說明他們關係相等。例如別名的存在就會破壞基於語法的檢查。

表達式檢查需要通過等價關係和表的對應關係推導而來。如果視圖和查詢中所有的表都是唯一的,那麼來自同一個表的列是等價的;如果視圖和查詢中有表出現多次,即 self join,那麼查詢到視圖的表的映射就存在多種可能,每種可能都需要進行一次改寫嘗試。有了視圖和查詢之間列的對應關係和上一節的等價關係,通過代數系統確定來自查詢的表達式是否和視圖中的表達式相等,或者能否從中計算出來。

存在一些啓發式的規則,允許一個表達式從另外的表達式中計算出來。比如算數規則從 x + 1 中計算出 x,例如 SUM(x) 和 COUNT(x) 計算出 AVG(x),還存在一些 Function Dependency 規則,例如時間函數,可以通過返回的天的結果計算年等。

謂詞檢查

視圖改寫需要物化視圖中存在查詢所有需要的行數,即視圖的謂詞的範圍大於或等於查詢的範圍。使用 Wq 表示查詢的謂詞,Wv表示視圖的謂詞,我們需要檢查 Wq => Wv,其中 => 表示 Wq 滿足 Wv 的含義。

優化器提取所有的謂詞,並將他們轉換爲 CNF 的形式,即 W=P1 ^ P2 ^ ... ^ Pn。將謂詞按照等值謂詞,範圍謂詞和剩餘謂詞進一步分爲 W= PE ^ PR ^ PU。PE,PR,PU 分別代表等值謂詞,範圍謂詞和剩餘謂詞。謂詞檢查變成了 PEq ^ PRq ^ PUq => PEv ^ PRv ^ PUv,由於正交的性質,最終將問題分解成 PEq => PEv,PEq ^ PRq => PRv,PEq ^ PUq => PUv 三類檢查。

查詢的等值謂詞用於推導等價關係。查詢中任何視圖不滿足等價關係的謂詞都構成補償謂詞,視圖中存在查詢無法滿足的等價關係則無法改寫。

範圍謂詞在優化器中可以存儲爲範圍的形式,可以方便的計算差集。對於查詢中每一個範圍謂詞,如果視圖存在對應的範圍精準匹配,不需要進行補償,否則視圖範圍必須大於查詢的範圍,並通過差集構造一個補償謂詞。

查詢表達式和試圖剩餘的謂詞共同構成剩餘謂詞PE,只能進行精準匹配。查詢中任何與視圖不匹配的謂詞都構成補償謂詞。查詢與視圖不匹配則無法改寫。

所有的補償謂詞都需要通過表達式檢查,確保可以從視圖的輸出中計算出來。

例如查詢

select l_orderkey, o_custkey, l_partkey,l_quantity*l_extendedprice
from lineitem, orders, part
where l_orderkey = o_orderkey
and l_partkey  = p_partkey
and l_partkey >= 150 and l_partkey <= 160
and o_custkey = 123
and o_orderdate = l_shipdate
and p_name like ‘%abc%’
and l_quantity*l_extendedprice > 100

物化視圖

create materialized view mview2
Enable Query Rewrite
as select l_orderkey, o_custkey, l_partkey,l_shipdate, o_orderdate,l_quantity*l_extendedprice as gross_revenue
from lineitem, orders, part
where l_orderkey = o_orderkey
and l_partkey  = p_partkey
and p_partkey >= 150
and o_custkey >= 50 and o_custkey <= 500
and p_name like ‘%abc%’

查詢可以被改寫爲

select l_orderkey, o_custkey, l_partkey, gross_revenue
from mview2
where l_partkey <= 160
and o_custkey = 123
and o_orderdate = l_shipdate
and gross_revenue > 100

Grouping 和 Aggregation 檢查

如果視圖和查詢帶有 GroupBy 或 Aggregation 函數,需要進行額外檢查:

  1. 檢查查詢請求的數據分組是否與物化視圖中存儲的數據分組相同,如果不同,優化器會嘗試對物化視圖進行彙總。
  2. 如果需要進一步彙總計算,所有需要的列的都可以從視圖輸出中計算出來。

例如查詢

select c_nationkey, sum(l_quantity*l_extendedprice)
from lineitem, orders, customer
where l_orderkey = o_orderkey
and o_custkey = c_custkey
group by c_nationkey

物化視圖

create materialized view mview3
enable Query rewrite as
select o_custkey, count_big(*) as cnt, sum(l_quantity*l_extendedprice) as revenue
from lineitem, orders
where l_orderkey = o_orderkey
group by o_custkey

查詢可以被改寫爲

select c_nationkey, sum(revenue)
from customer join mview3 on c_custkey = o_custkey
group by c_nationkey

如果分組列表不同,只能改寫 Group By 在查詢最外部的情況,否則無法進行進一步聚合,無法滿足第二個改寫要求。只有特定的聚合函數支持進一步聚合,常見的聚合函數有 MIN,MAX,SUM,COUNT。

例如如下查詢

select o_custkey, count(*) as cnt, sum(l_quantity*l_extendedprice) as revenue
from lineitem, orders
where l_orderkey = o_orderkey
group by o_custkey

物化視圖

create materialized view mview4 
enable Query rewrite as
select o_custkey, l_partkey, count(*) as cnt, sum(l_quantity*l_extendedprice) as revenue
from lineitem, orders
where l_orderkey = o_orderkey
group by o_custkey, l_partkey

可以被改寫爲

select c_nationkey, sum(cnt) as cnt, sum(revenue) as revenue
from mview4
where c_custkey = o_custkey
group by o_custkey

如果聚合函數在視圖中不存在,可以通過一些規則進行計算,例如從 SUM(x) 和 COUNT(x) 計算出 AVG(x),從 SUM(x) + SUM(y) 中計算出 SUM(x + y) ,這些依賴表達式改寫檢查中啓發式的規則。

高級改寫規則

如果物化視圖和查詢不滿足上一節的改寫規則,還可以通過其他規則進行轉換。

Join 補償

如果只考慮 Inner Join,視圖和查詢的 Join 關係 不一致有兩種情況:查詢比視圖包含更多的聯接,或者視圖包含更多的聯接。

如果查詢比視圖包含更多的表,我們可以簡單把缺少的 Join 添加在視圖之上,使其滿足改寫要求。Join 條件會通過謂詞改寫正確的添加進來,優化器也可以通過後續規則調整計劃。

Join 補償使優化器可以更早的進行匹配改寫,減少改寫的嘗試次數。

例如如下查詢

select c_custkey, c_name, l_orderkey,l_partkey, l_quantity
From lineitem, customer, orders
Where l_orderkey = o_orderkey
And o_custkey = c_custkey
Where l_orderkey between 1000 and 1500
And l_shipdate = l_commitdate

物化視圖

Create materialized view mview5
Enable query rewrite as
Select l_orderkey,l_partkey, l_quantity
From lineitem, orders
Where l_orderkey = o_orderkey
And o_orderkey >= 500

可以被改寫爲

Select l_orderkey, l_partkey, l_quantity
From mview5 join customer on o_custkey = c_custkey
Where l_orderkey between 1000 and 1500
And l_shipdate = l_commitdate

Join 消除

如果一個 Join 出現在視圖中但是沒有出現在查詢中,可以嘗試使用 Join 消除規則。Join 消除是優化器優化中的一項常見方法,如果 Join 滿足以下5個條件,則表 T1 在與表 T2 的聯接中時不變的:

  1. 聯接條件是一個簡單的等值條件
  2. T1.fk 是 T2.pk 的外鍵
  3. T1.fk 滿足非 null 約束
  4. T2 沒有任何的謂詞
  5. T2 在與 T1 以外的表的聯接是不變的

這意味着表 T1 在與 T2 的 Join 關係不會影響 T1 的結果,視圖中的 T2 可以在 Join Graph 中忽略。不變聯接的存在允許在基礎物化視圖上創建更大的並集或超集,從而允許物化視圖包含更大的預計算,也可以改寫更多的查詢。

例如查詢

Select l_orderkey, l_partkey, l_quantity
From lineitem
Where l_orderkey between 1000 and 1500
And l_shipdate = l_commitdate

物化視圖

Create materialized view mview6
Enable query rewrite as
Select c_custkey, c_name, l_orderkey,l_partkey, l_quantity
From lineitem, orders, customer
Where l_orderkey = o_orderkey
And o_custkey = c_custkey
And o_orderkey >= 500

可以被改寫成

select l_orderkey, l_partkey, l_quantity
from mview6
where l_orderkey between 1000 and 1500
and l_shipdate = l_commitdate

Join 派生

如果存在 Outer Join,視圖和查詢 Join 關係不一致,可以嘗試利用 Join 派生性,從物化視圖中的聯接重新計算查詢中的聯接。例如,能從物化視圖中的 left Outer Join 的結果裏,計算 Inner Join,Anti Join 的結果,Inner Join 又能進一步計算 Semi Join,這樣就能使用物化視圖過濾某些行來回答不同具有不同 Join 關係的查詢。

Join 派生可以擴展改寫的範圍,允許優化器將基於物化視圖的改寫與解關聯的規則規則結合,改寫帶有 IN,EXISTS 等的查詢,也可以避免其他優化規則對 Join 的調整,例如 EliminateOuterJoin 和 PredicatePushDown 可能會將 outer join 優化成 inner join。

Union 改寫

物化視圖只包含一部分查詢所需的數據,也可以用於查詢改寫。在很多場景下,物化視圖不會也無法存儲全部的數據。

一個典型的情況是,數據在不斷的寫入,但是寫入只發生在最近一段時間內。在持續刷新的表上構建全量物化視圖,這可能導致因爲數據插入視圖頻繁刷新,產生高昂的刷新成本,甚至視圖因爲持續刷新而完全不可用。更好的做法是構建一個 T+1 條件刷新的物化視圖,存儲不變的數據,可以降低刷新成本。

另一種常見的情況是,數據倉庫存儲全量的數據,而查詢集中在最近幾個月的數據,構建全部數據的物化視圖成本過於高昂,物化視圖只構建最近幾個月數據,也能改寫絕大多數查詢。

Union 改寫會嘗試使用視圖回答部分查詢,減少查詢中實時計算的數據量。Union 改寫可以進一步應用 Aggregation 改寫,支持使用物化視圖部分數據回答聚合查詢。

例如查詢

select l_orderkey, l_partkey, l_quantity
from lineitem
where l_orderkey l_orderkey > 500 and l_orderkey <= 1500
and l_shipdate = l_commitdate

物化視圖

create materialized view mview8
enable query rewrite
as
select l_orderkey, l_partkey, l_quantity
from lineitem
where l_orderkey > 1000
and l_shipdate = l_commitdate

改寫結果

select l_orderkey, l_partkey, l_quantity
from lineitem
where l_orderkey > 500 and l_orderkey <= 1000
and l_shipdate = l_commitdate
union
select o.shippriority
from mview8
where l_orderkey > 1000 and l_orderkey <= 1500

查詢改寫的實現

視圖改寫通常有三種查詢改寫的實現方式:

  1. 基於語法的改寫
  2. 基於規則的改寫
  3. 基於結構的改寫

基於語法的改寫

文本匹配或者語法匹配是最簡單的改寫方法,將查詢的文本與物化視圖的文本或語法樹進行比較,完全匹配可以進行改寫。這種改寫只能匹配完整的查詢語句或子語句,細微的變化就會導致查詢無法改寫,適用的範圍很小。基於語法的改寫雖然簡單,但是效率很高,改寫的成本可以忽略不計。

基於規則的改寫

基於規則的改寫和其他優化器規則相同,針對不同 Pattern 的查詢和視圖編寫不同的規則,尋找等價的替代關係樹。

最簡單的一條規則就是直接比較子查詢和視圖的計劃,如果相同就能改寫。高級的改寫規則不需要物化視圖等同於被替換的計劃,會嘗試計算補償謂詞,構建等價查詢表達式。例如 Join 改寫,比較 Join 查詢的子表達式是否和視圖 Join 的某個子表達相同或者能否從中計算出來,每一個Join子表達式都存在映射關係,最後檢查補償表達式能否從視圖中計算得到。

基於規則的改寫可以實現大量重寫,實現也比較簡單,改寫匹配速度快,但是也存在侷限性。這種改寫依賴轉換規則來尋找等價關係,因此需要窮舉所有可能的轉換關係來實現複雜視圖的重寫。一些複雜的視圖不可能窮舉所有的等價關係,例如存在很多的 Join 聯接或者複雜的 Project 關係,基於規則的改寫適用的範圍取決於規則的數量。

基於結構的改寫

基於結構的改寫與基於規則的改寫相反,通過提取查詢中的特徵,使用一套規則進行匹配改寫。優化器將查詢表示爲 SPJG 標準形式 (
Join-Select-Project-GroupBy),提取查詢中的 Join,Projects,Filters,Grouping 和 Aggregations 五種表達式,分別與物化視圖對應的表達式進行匹配和改寫。

這個方法是由微軟在 2001 年 SIGMOD 論文《Optimizing queries using materialized views: A practical, scalable solution》系統化的提出。這種方法可以改寫包含可以改寫包含 Join,Filter,Project 的任意查詢的方法,運用一系列的步驟匹配並得到補償表達式。還可以進一步改寫含有 Aggreagtion 的查詢,在需要時添加 Aggregation 節點返回進一步彙總的結果。

基於結構的改寫很容易擴展,例如改寫 Outer Join 和子查詢等,可以完成幾乎全部的改寫。但是搜索成本較高,尤其是在查詢複雜,改寫嘗試次數很多的情況下。

參考資料

  1. Goldstein J, Larson P Å. Optimizing queries using materialized views: a practical, scalable solution[J]. ACM SIGMOD Record, 2001, 30(2): 331-342.
  2. Bello R G, Dias K, Downing A, et al. Materialized views in Oracle[C]//VLDB. 1998, 98: 24-27.
  3. Zhou J, Larson P A, Goldstein J, et al. Dynamic materialized views[C]//2007 IEEE 23rd International Conference on Data Engineering. IEEE, 2007: 526-535.
  4. Jindal A, Qiao S, Patel H, et al. Computation reuse in analytics job service at microsoft[C]//Proceedings of the 2018 International Conference on Management of Data. 2018: 191-203.
  5. Calcite: Materialized View.
  6. Oracle: Database Data Warehousing Guide: Advanced Query Rewrite for Materialized Views.
  7. Redshift: Automatic query rewriting to use materialized views.
  8. Snowflake: Creating and Working With Materialized Views.

作者:阿里雲數據庫OLAP產品部 雲曦

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

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