1. 程式人生 > 實用技巧 >MySQL之謂詞下推

MySQL之謂詞下推

MySQL之謂詞下推

什麼是謂詞

在SQL中,謂詞就是返回boolean值即true或者false的函式,或是隱式轉換為boolean的函式。SQL中的謂詞主要有 LKIE、BETWEEN、IS NULL、IS NOT NULL、IN、EXISTS

謂詞下推的基本思想即:

將過濾表示式儘可能移動至靠近資料來源的位置,以使真正執行時能直接跳過無關的資料。

傳統資料庫中的謂詞下推:

在傳統資料庫的查詢系統中謂詞下推作為優化手段很早就出現了,謂詞下推的目的就是通過將一些過濾條件儘可能的在最底層執行可以減少每一層互動的資料量,從而提升效能。例如下面這個例子:

select count(1) from A Join B on A.id = B.id where A.a > 10 and B.b < 100;

在處理Join操作之前需要首先對A和B執行TableScan操作,然後再進行Join,再執行過濾,最後計算聚合函式返回,但是如果把過濾條件A.a > 10和B.b < 100分別移到A表的TableScan和B表的TableScan的時候執行,可以大大降低Join操作的輸入資料。優化後的語句如下:

select count(1) from (select *  from A  where a>10)A1 Join (select *  from B  where b<100)B1 on A1.id = B1.id;

無論是行式儲存還是列式儲存,都可以在將過濾條件在讀取一條記錄之後執行以判斷該記錄是否需要返回給呼叫者,在Parquet做了更進一步的優化,優化的方法時對每一個Row Group的每一個Column Chunk在儲存的時候都計算對應的統計資訊,包括該Column Chunk的最大值、最小值和空值個數。通過這些統計值和該列的過濾條件可以判斷該Row Group是否需要掃描。另外Parquet未來還會增加諸如Bloom Filter和Index等優化資料,更加有效的完成謂詞下推。

在使用Parquet的時候可以通過如下兩種策略提升查詢效能:

1、類似於關係資料庫的主鍵,對需要頻繁過濾的列設定為有序的,這樣在匯入資料的時候會根據該列的順序儲存資料,這樣可以最大化的利用最大值、最小值實現謂詞下推。

2、減小行組大小和頁大小,這樣增加跳過整個行組的可能性,但是此時需要權衡由於壓縮和編碼效率下降帶來的I/O負載。

列式儲存中的謂詞下推思想

RF演算法中,用了謂詞下推思想。大小表進行broadcast hash join時,用小表的join列資料構建BloomFilter,廣播到大表的所有partition,使用該BloomFilter對大表join列資料進行過濾。最後將大表過濾後得到的資料與小表資料進行hashJoin。

這個過程如下圖:

這樣的好處是:

  • 在儲存層即過濾了大量大表無效資料,減少掃描無效資料列的同行其他列資料IO
  • 減少儲存程序到計算程序傳輸的資料
  • 減少hashjoin開銷

如這個SQL:

select item.name, order.* from order , item where order.item_id = item.id and item.category = ‘book’

使用謂詞下推,會將表示式 item.category = ‘book’下推到join條件order.item_id = item.id之前。再往高大上的方面說,就是將過濾表示式下推到儲存層直接過濾資料,減少傳輸到計算層的資料量。

HIVE中的謂詞下推(下推規則同樣適用於SparkSQL)

​ Hive中的Predicate Pushdown簡稱謂詞下推,簡而言之,就是在不影響結果的情況下,儘量將過濾條件提前執行。謂詞下推後,過濾條件在map端執行,減少了map端的輸出,降低了資料在叢集上傳輸的量,節約了叢集的資源,也提升了任務的效能。

​ 具體配置項是hive.optimize.ppd,預設為true,即開啟謂詞下推

​ PPD規則:

​ 規則的邏輯描述如下:

  • During Join predicates cannot be pushed past Preserved Row tables.

​ join條件過濾不能下推到保留行表中。

比如以下選擇,left join中左表s1為保留行表,所以on條件(join過濾條件)不能下推到s1中

select s1.key, s2.key from src s1 left join src s2 on s1.key > '2';

而s2表不是保留行,所以s2.key>2條件可以下推到s2表中:

select s1.key, s2.key from src s1 left join src s2 on s2.key > '2';
  • After Join predicates cannot be pushed past Null Supplying tables.

​ where條件過濾不能下推到NULL補充表。

比如以下選擇left join的右表s2為NULL補充表所以,s1.key>2 where條件可以下推到s1:

select s1.key, s2.key from src s1 left join src s2 where s1.key > '2';

而以下選擇由於s2未NULL補充表所以s2.key>2過濾條件不能下推

select s1.key, s2.key from src s1 left join src s2 where s2.key > '2';

關於join和where採用ppd的規則如下:

1、對於Join(Inner Join)、Full outer Join,條件寫在on後面,還是where後面,效能上面沒有區別;

2、對於Left outer Join ,右側的表寫在on後面、左側的表寫在where後面,效能上有提高;

3、對於Right outer Join,左側的表寫在on後面、右側的表寫在where後面,效能上有提高;

4、所謂下推,即謂詞過濾在map端執行;所謂不下推,即謂詞過濾在reduce端執行

注意:如果在表示式中含有不確定函式,整個表示式的謂詞將不會被pushed,例如

select a.* from a join b on a.id = b.idwhere a.ds = '2019-10-09' and a.create_time = unix_timestamp();

因為unix_timestamp是不確定函式,在編譯的時候無法得知,所以,整個表示式不會被pushed,即ds='2019-10-09'也不會被提前過濾。類似的不確定函式還有rand()等。