Impala源碼之資源管理與資源隔離

本文由  網易雲 發佈。

 

前言

 

Impala是一個MPP架構的查詢系統,爲了作到平臺化服務,首先須要考慮就是如何作到資源隔離,多個產品之間儘量小的甚至毫無影響。對於這種需求,最好的隔離方案無疑是物理機器上的隔離,A產品使用這幾臺機器,B產品使用那幾臺機器,而後前端根據產品路由到不一樣集羣,這樣能夠作到理想中的資源隔離,可是這樣極大的增長了部署、運維等難度,並且沒法實現資源的共享,即便A產品沒有任務在跑,B產品也不能使用A產品的資源,這無疑是一種浪費。毛主席教導咱們浪費是可恥的,因此咱們要想辦法在充分利用資源的狀況下實現產品之間的資源隔離,這實際上是一個很是有難度的工做。前端

 

YARN

 

在大數據生態圈,談到資源管理(Resource Management)和隔離(Resource Isolation),第一反應想到的確定是YARN,它是自Hadoop2.0開始而且一直使用的一種資源管理系統, YARN主要經過集中式的資源管理服務Resource Manager管理系統中所有的資源(主要是CPU和內存),而後爲每個產品或者業務定義一個隊列,該隊列中定義了提交到該隊列下的任務最大申請的資源總量;當一個任務提交到Resource Manager以後,它會啓動一個ApplicationMaster 來負責該任務的資源申請和調度,而後根據任務須要的資源需求,向Resource Manager申請資源,Resource Manager 根據當前隊列中資源剩餘狀況判斷是否授予資源,若是當前隊列資源已經被用盡則該任務須要等待直到有資源釋放,等到ApplicationMaster申請到資源以後則請求NodeManager啓動包含必定資源的Container,Container利用cgroups輕量級的隔離方案實現,爲了根據不一樣的使用場景YARN也集成了不一樣的分配和調度策略,典型的有Capacity Scheduler和Fair Scheduler。緩存

 

上圖展現了客戶端提交任務到YARN的流程,平時在提交MR、spark任務時也是經過這種方式,可是對於MPP架構的系統,它的查詢響應時間由最慢的節點運行時間決定,而爲了提高查詢性能,又須要儘量多的節點參與計算,而YARN上的任務每次都是啓動一個新的進程,啓動進程的時間對於批處理任務是能夠接受的,畢竟這種任務運行時間比較久,而對於追求低延遲的Ad-hoc查詢而言代價有點大了,極可能出現進程啓動+初始化時間大於真正運行的時間。網絡

 

除了使用原生的yarn調度,impala也嘗試過使用一個稱之爲Llama(Long-Lived Application Master)的服務實現資源的管理和調度,它實際上是YARN上的一個ApplicationMaster,實現impala和yarn之間的協調,當一個impala接收到查詢以後,impala根據預估的資源需求向Llama請求資源,後者向YARN的Resource Manager服務申請可用的資源。可是前面提到了,impala爲了保證查詢速度須要全部的資源同時得到,這樣才能推動下一步任務的執行,實際上,Llama實現了這樣的批量申請的功能,因此一個查詢的進行須要等到一批資源同時到達的時候纔可以進行下去,除此以外,Llama還會緩存申請到的資源。可是Llama畢竟仍是須要從YARN申請資源而且啓動進程,仍是會存在延遲比較大的問題,所以,Impala也在2.3版本以後再也不支持Llama了。session

 

Impala資源隔離

 

目前Impala的部署方式仍然是啓動一個長時間運行的進程,對於每個查詢分配資源,而在新版本(2.6.0之後),加入了一個稱爲Admission Control的功能,該功能能夠實現必定意義上的資源隔離,下面咱們就深刻了解一下這個機制,看一下它是如何對於資源進行隔離和控制的。架構

首先,若是根據impala的架構,全部的SQL查詢,從解析、執行計劃生成到執行都是在impalad節點上執行的,爲了實現Admission Control,須要在impalad配置以下了兩個參數:併發

1. --fair_scheduler_allocation_path 該參數是用來指定fair-scheduler.xml配置文件路徑,該文件相似於YARN的fair- scheduler.xml配置,具體配置內容下面再詳細講述;運維

2. --llama_site_path 該參數用來指定Llama的配置文件llama-site.xml,上面不是說到新版本不用Llama了嗎?爲何還要配置它呢,其實這裏面的配置項都是一些歷史遺留項了吧。oop

接下來就詳細介紹一下如何配置這兩個文件,對於第一個文件fair-scheduler.xml,熟悉YARN的都知道該文件實現公平調度器的配置,YARN中的公平調度是如何實現的我不懂,可是文件中基本上須要配置一下每個隊列的資源分配狀況,下面是一個配置實例: 性能

 

<queue name="sample_queue">大數據

<minResources>10000 mb,0vcores</minResources>

<maxResources>90000 mb,0vcores</maxResources>

<maxRunningApps>50</maxRunningApps>

<weight>2.0</weight>

<schedulingPolicy>fair</schedulingPolicy>

<aclSubmitApps>charlie</aclSubmitApps>

</queue>

 

可是經過impala源碼發現impala中用到的每個隊列的配置只有aclSubmitApps和maxResources,前者用於肯定該隊列能夠由哪些用戶提交任務,若是用戶沒有該隊列的提交權限(隊列中沒設置),或者用戶沒指定隊列則提交到default隊

列,若是default隊列不存在或者用戶沒有提交到的default隊列的權限,則拒絕該請求;後者是用於肯定該隊列在整個集羣中使用的最大資源數,目前impala關注的資源只有memory。在上例中sample_queue隊列在整個集羣中可以使用的內存大小是90GB,只有charlie用戶可以提交到該隊列。

既然只用到這兩個配置,爲何impala不單獨搞一個配置格式呢,而選擇直接用fair-schedular.xml呢?我想一方面是爲了省去本身寫解析類了,直接使用yarn的接口就能夠了,另外爲之後更加完善作準備。下面再看一下Llama配置中用到了什麼配置,配置實例以下:

 

<property>

<name>llama.am.throttling.maximum.placed.reservations.root.default</name>

<value>10</value>

</property>

<property>

<name>llama.am.throttling.maximum.queued.reservations.root.default</name>

<value>50</value>

</property>

<property>

<name>impala.admission-control.pool-default-query-options.root.default</name>

<value>mem_limit=128m,query_timeout_s=20,max_io_buffers=10</value>

</property>

<property>

<name>impala.admission-control.pool-queue-timeout-ms.root.default</name>

<value>30000</value>

</property>

 

 

這些配置的意義以下,具體的配置項則是以下key後面再加上隊列名:

 

//隊列中同時在跑的任務的最大個數,默認是不限制的llama.am.throttling.maximum.placed.reservations

//隊列中阻塞的任務的最大個數,默認值是200 llama.am.throttling.maximum.queued.reservations

//隊列中阻塞的任務在阻塞隊列中最大的等待時間,默認值是60s

 

Impala實現

 

 

好了 , 分析完了Admission Control中使用的配置項,用戶能夠在建立一個session以後經過set REQUEST_POOL=pool_name的方式設置改session的請求提交的隊列,固然若是該用戶沒有該隊列的提交權限,以後執行都會失敗。下面根據查詢的流程看一下impala如何利用這些參數完成資源隔離的 當impala接收到一個查詢請求以後,請求除了包含查詢SQL以外,還包括一批查詢參數,這裏咱們關心的是該請求提交的隊列(REQUEST_POOL參數),它首先根據查詢執行的用戶和隊列參數得到該查詢應該提交到的隊列,選取隊列的規則以下:

1. 若是服務端沒有配置fair-scheduler.xml和llama-site.xml,說明沒有啓動資源控制服務,則全部的請求都提交到一個名爲default-pool的默認隊列中;

2. 若是該查詢沒有指定REQUEST_POOL,則將REQUEST_POOL設置爲yarn默認隊列default。

判斷隊列名是否存在,而後再根據當前提交任務的用戶和隊列名判斷該用戶是否具備提交任務到隊列的權限。若是隊列名不存在或者該用戶無權限提交則查詢失敗。

查詢執行完初始化工做(選擇隊列只是其中的一部分工做)以後會調用FE的GetExecRequest接口進行執行計劃的生成, 生成執行計劃的流程大體分爲三部:

1. 語法分析生成邏輯執行計劃並進行預處理;

2. 根據邏輯執行計劃生成單機執行計劃;

3. 將單機執行計劃轉換成物理自行計劃,後續再單獨介紹這部分。

 

接着impalad節點會根據執行計劃判斷該查詢是否能夠繼續執行,只有出現以下幾種狀況時,查詢須要排隊:

1. 當前隊列中已經有查詢在排隊,由於阻塞隊列是FIFO調度的,因此新來的查詢須要直接排隊;

2. 已經達到該隊列設置的併發查詢上線;

3. 當前查詢須要的內存不可以獲得知足。

 

前兩種條件比較容易判斷,對於第三種狀況,impala須要知道當前查詢須要的內存和當前隊列中剩餘的內存狀況進行判斷,這裏的內存使用分爲兩個方面:集羣中隊列剩餘的總內存和單機剩餘內存。首先判斷隊列中剩餘內存和當前查詢須要在集羣中使用的內存是否達到了隊列設置的內存上限;而後在判斷該查詢在每個impalad節點上須要的內存和該節點剩餘內存是否達到設置的內存上限(節點的內存上限是由參數mem_limit設置的)。那麼問題又來了,該查詢在整個集羣須要多少內存,在每個節點上須要多少內存是如何計算的呢?對於整個集羣上須要內存其實就是每個節點須要的內存乘以須要的節點數,那麼核心問題就是該查詢須要在每個節點使用的內存大小。

 

可能你們和我同樣以爲,對於每個查詢須要在每個節點消耗的內存是根據查詢計劃預估出來的,可是這樣作是很是難的,那麼來看一下當前impala是如何作的,對於單節點內存的預估,按照以下的優先級計算查詢須要的單機內存:

1. 首先判斷查詢參數rm_initial_mem是否被設置(能夠經過set rm_initial_mem =xxx設置),若是設置了則直接使用該值做爲預估值;

2. 而後判斷impalad啓動的時候是否設置rm_always_use_defaults=true,若是設置了則使用rm_default_memory中配置的內存大小;

3. 接着再判斷該session是否設置了mem_limit(能夠經過set mem_limit=xx設置,注意它和impalad啓動時的mem_limit配置的區別),若是設置則使用該值;

4. 最後根據判斷執行計劃中是否計算出了每個節點須要分配的內存大小;

5. 若是以上都沒有命中則使用默認的rm_default_memory配置(impalad啓動時候的參數),該值默認值是「4GB」。

 

從上面的的判斷邏輯來看,impalad最後纔會根據執行計劃中預估的值肯定每個節點分配的內存大小,畢竟只是根據統計信息預估出來的信息並非準確的,對於一些複雜的查詢而言,多是偏差很是大的。

好了,經過上面的分析,整個查詢審計流程梳理了一遍,可是若是當前資源不可以知足該查詢呢?此時就須要將該查詢放入隊列中,該查詢則會阻塞直到知足以下兩種條件之一:

1. 該查詢所使用的隊列擁有了該查詢須要的足夠的資源;

2. 該查詢存放在隊列中並超時,超時時間是由隊列中的queue_timeout_ms參數設置,若是隊列沒設置則由impalad啓動時的queue_wait_timeout_ms參數決定,默認是60s。

 

再談Statestore

 

咱們這裏就不深究執行計劃中的內存估計是如何計算的了,可是還有一個比較重要的問題:每個impalad是獨立工做的,只有在須要分配任務的時候纔會通知其他的impalad執行相應的operation,那麼impalad如何知道其餘impalad節點的資源狀態的?包括每個隊列已使用的內存大小,每個節點已使用的內存大小等。這就靠咱們上一篇文章中介紹的statestored了,在statestored中impalad啓動時會註冊一個impala-request-queue主題,每個impalad都是該topic的發佈者同時也是訂閱者,週期性的發佈當前節點的內存使用狀況,而後每個impalad節點再根據topic中最新的信息更新整個集羣的資源使用狀態。這種交互方式的確挺方便,可是可能存在必定的不肯定性,例如某一次impalad與statestored 的網絡抖動都有可能致使沒法獲取到最新的資源使用狀態。

 

硬性限制

 

impalad使用Admission Control實現了必定意義上的資源隔離,可是這畢竟是軟性的,而不是像YARN那種經過cgroup 啓動新進程來進行隔離,仍然有可能存在一個比較繁重的查詢將整個集羣搞垮,對於這種狀況做爲查詢平臺咱們須要作到即便返回錯誤也不能影響這個整個集羣的服務,此時就須要祭出impala查詢的一個關鍵性參數:mem_limit,對於impalad中的每個模塊,啓動時均可以設置mem_limit參數,該參數是每個執行節點可以分配的最大內存(tcmalloc 管理),而每個查詢也能夠設置mem_limit,它表示該查詢在每個節點上最大分配的內存大小,再每個impalad執行查詢(impala中稱之爲fragment)的時候會分配一個大小爲mem_limit的block pool,須要的內存從pool中分配並保存在內存中,若是分配的內存超出pool的大小,則選擇必定的block spill到外存中,這部分具體的執行流程也是很是複雜了,能夠單獨再講,這部分block在須要的時候再從本地磁盤讀取到內存中,須要spill到外存的查詢無疑會拖慢查詢速度, 可是這很好的保存了整個系統的可用性和穩定性。

 

總結

 

在實際部署的時候,咱們會根據每個用戶的數據量大小、業務類型分配隊列,甚至相同的業務不一樣時間區間的查詢分配不一樣的隊列,而且詳細設置該隊列中的默認查詢參數,尤爲是mem_limit參數和最大併發數,這樣能夠較好的限制租戶之間的影響,爲了不惡意用於的使用,能夠限制用戶本身設置MEM_LIMIT參數,儘量得保證集羣的穩定性。

 

網易有數:企業級大數據可視化分析平臺。面向業務人員的自助式敏捷分析平臺,採用PPT模式的報告製做,更加易學易用,具有強大的探索分析功能,真正幫助用戶洞察數據發現價值。可點擊這裏免費試用

 

 

瞭解 網易雲 :
網易雲官網:https://www.163yun.com/
新用戶大禮包:https://www.163yun.com/gift
網易雲社區:https://sq.163yun.com/

相關文章
相關標籤/搜索