1. 程式人生 > 其它 >Impala系列:Impala查詢優化

Impala系列:Impala查詢優化

==========================
理解 mem_limit 引數
==========================
set mem_limit=-1b #取消記憶體限制
set mem_limit=1gb #設定單機記憶體上限為1GB, 注意是單機
set mem_limit=1mb #設定單機記憶體上限為1MB, 注意是單機
如果設定了 mem_limit, impala 將跳過Query記憶體評估環節, 直接檢查Pool中剩餘記憶體是否夠用, 如果夠用的話, 將直接執行. 如果不夠用的話, 將按照pool設定的策略, 將Query放到queue佇列中, 如果在timeout設定時間內仍得不到足量的記憶體資源, 該Query將被取消. 如果使用者不設定mem_limit, 預設使用Pool的 default_pool_mem_limit 值, 如果default_pool_mem_limit沒有設定,Impala會自己來估計這個值. 如果沒有設定mem_limit, 否則Impala根據統計資訊和執行計劃預估記憶體消耗.

當然, 在執行期間, 如果Query佔用的記憶體超過 mem_limit, impala 將終止該 query,

假設單機設定了10GB的記憶體上線, 叢集有10個節點, 在查詢之前, Impala檢查Pool 中剩餘記憶體是否夠300GB.

Impala pool 級別幾個設定引數有:
default_pool_max_queued, 進入等待佇列中查詢的缺省個數
default_pool_max_requests, 正在執行中的查詢個數
default_pool_mem_limit, 為查詢設定的預設記憶體佔用上限, 通常通過 disable_pool_mem_limits 引數禁用了該設定.
queue_wait_timeout_ms, 預設 60000 毫秒, 即1分鐘, 查詢在佇列中最大等待時長, 超過該設定值, 查詢就被reject.


手工設定 MEM_LIMIT 引數的必要性
1. 避免Impala的 over-estimation 的不良後果
Impala 在查詢執行之前會預測SQL執行會消耗多大記憶體, 預測主要是依據表的統計資訊, 但在很多時候, Impala的預估是很粗放式的, 即使表的統計資訊很及時, impala也會over estimate記憶體消耗. over-estimation 的後果有: (1)預估值如果大於impala pool中的剩餘記憶體, impala 就會reject該查詢. (2)降低併發度.
2. 有可能會提升執行速度
手工設定 MEM_LIMIT 引數後, Impala將會跳過記憶體預測過程, 有可能會加快執行速度.

如何設定 MEM_LIMIT 引數?
先試執行一下SQL, 然後在 profile 中, 檢視其 memory_per_node_peak 值, 該值即為實際上的記憶體消耗, 或者在 impala WebUI該query詳細頁面的 Summary 頁籤的 Peak Mem 欄位.


==========================
impala 資源的軟隔離
==========================
摘自國雙公司的說明: https://blog.csdn.net/qq_18882219/article/details/78447558

由於Impala的每個Impalad節點都可以接受查詢,對於每個Pool現在有多少查詢,佔了多少記憶體,Queue了多少,這些資訊也是每個Impalad更新,通過Statestored來廣播到其他Impalad,所以這個資訊可能在每個節點上可能是不一致的。當一個Impalad收到查詢需要做一些決策例如是否拒絕,是否Queue住,本地的這個決策資訊可能是舊的,所以Impala基於Pool的資源隔離本身來說是一種軟隔離,也就是說對於任何一個Pool來說,其用到的記憶體有可能會超過最大記憶體,執行的查詢數量有可能會超過Pool設定的最大查詢數量。這個我們在實際的使用中也證明了。軟隔離問題會帶來兩個風險:
1.單個節點申請的記憶體在某個時刻超過了分配給Impalad程序的記憶體,這個會導致Impalad OOM退出
2.某個Pool在某一個時刻使用了遠遠超過這個Pool的資源,這個對於不同業務用Pool來做資源隔離是不利的。
這個問題我們也跟Impala社群的開發者做過討論,最後使用的方案是:單個Pool指定唯一的Coordinator,這個Pool的所有查詢都發送給同一個Impalad。於是這個Coordinator時刻都有這個資源池最新的資訊,就從軟隔離進化成了硬隔離,缺點是會帶來單點問題,可採取了主備的方式來避免這一問題.


==========================
其他幾個重要的session 變數
==========================
除了 MEM_LIMIT, 還有如下常用的session 變數
EXPLAIN_LEVEL :設定explain和profile的輸出詳略
DISABLE_UNSAFE_SPILLS : 是否禁用 disk spill, 目前對disk spill限制還很多, 比如非等值join不能使用 disk spill.
REQUEST_POOL : 設定所在的佇列


EXPLAIN_LEVEL 引數可以控制 explain語句的輸出, 也可以控制 profile 命令輸出. 需要說明的是, explain 可以在SQL客戶端中執行, 而profile 命令只能在impala shell中執行, 另外只能展現已經最近執行完畢的那個SQL的profile.
set EXPLAIN_LEVEL = 0 -- 0 or MINIMAL, 因為輸出資訊較少, 更容易發現主要的資訊 比如join的order
set EXPLAIN_LEVEL = 1 -- 1 or STANDARD, 標準的輸出
set EXPLAIN_LEVEL = 2 -- 2 or EXTENDED, 詳細的輸出
set EXPLAIN_LEVEL = 3 # 3 or VERBOSE, 更詳細的輸出

如果查詢速度較差, 可以通過下面命令看看是否缺少統計資訊.
set explain_level=3 --verbose 級, 包含更多的細節資訊. 在explain輸出中, 如果有如下資訊, 則對應表沒有統計資訊.
cardinality: unavailable
table stats: unavailable
column stats: unavailable

==========================
Join reordering
==========================
對於多表Join 查詢, 老版的impala總是按照表出現的順序依次查詢, 但新版Impala執行引擎已經可以自動按照的表/列的統計資訊, 做Join reordering. 規則是:
大表(表 size和distinct value多的表)先被查詢, 小表後被查詢
無統計資訊的表(impala認為表的size為0)將最後被查詢.


==========================
使用 STRAIGHT_JOIN Hint
==========================
當統計資訊過期或沒有統計資訊或者表有很詭異的資料分佈, Impala執行計劃的查詢表順序很可能不是最優, 這時候, 最好加上 STRAIGHT_JOIN Hint, 強制impala按照SQL中表出現的次序做查詢, 當然我們SQL表出現次序應該是精心調整過的.

另外, STRAIGHT_JOIN 僅僅對於當前Select 語句有效, 如果子查詢和view也需要嚴格按照表次序做查詢, 需要在子查詢和檢視中顯式地加上 STRAIGHT_JOIN hint, 另外 STRAIGHT_JOIN hint 是impala 的關鍵詞, 不能放在/*+*/中.

select STRAIGHT_JOIN t1.* from t1
join t2 on t1.id=t2.id

select distinct STRAIGHT_JOIN t2.id,t1.name from t1
join t2 on t1.id=t2.id
;

==========================
join 的演算法
==========================
1. hash join: 對於等值join, impala將採用hash的方式處理, 具體又分兩種策略, broadcast 和 Shuffle.
broadcast join 非常適合右表是小表的情形, impala 先將右表複製到各個節點, 再和左表做join.
shuffle join, 也叫做partitioned join, 適合大表和大表關聯. 注意 partitioned join 和右表的 partition 沒有直接關係, impala 會將右表打散成N份, 傳送到左表所在的節點, 然後作join.
2. nested loop join: 針對非等值join, impala將使用 nested loop join, 這時我們不能設定 SHUFFLE/BROADCAST hint, 也不能使用 spill disk 功能. impala的非等值join的效率較低, Vertica的效率非常高, Hive直接不支援.

SELECT STRAIGHT_JOIN select_list FROM
join_left_hand_table
JOIN [{ /* +BROADCAST */ | /* +SHUFFLE */ }]
join_right_hand_table
remainder_of_query;

/* +SHUFFLE */ 即 partitioned join, 是將要關聯的兩個表按照
/* +BROADCAST */ 即
Exchange : the intermediate results are transmitted back to the coordinator node (labelled here as the EXCHANGE node)


==========================
最佳實踐
==========================
Impala執行引擎還不是那麼智慧, 多表join 的SQL最好還是按照下面的推薦的寫法:
1. 最大的表應該放在表清單的最左邊.
2. 多個join的查詢語句, 應該將選擇性最強的join放在最前面.
3. 定期對錶收集統計資訊, 或者在大量DML操作後主動收集統計資訊.
4. 在一個單一的查詢裡面, 參加join的表個數儘量不要超過4個, 不然效率比較低下.