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()等。