由“Beeline連接HiveServer2后如何使用指定的隊列(Yarn)運行Hive SQL語句”引發的一系列思考


背景
 
我們使用的HiveServer2的版本為0.13.1-cdh5.3.2,目前的任務使用Hive SQL構建,分為兩種類型:手動任務(臨時分析需求)、調度任務(常規分析需求),兩者均通過我們的Web系統進行提交。以前兩種類型的任務都被提交至Yarn中一個名稱為“hive”的隊列,為了避免兩種類型的任務之間相互受影響以及並行任務數過多導致“hive”隊列資源緊張,我們在調度系統中構建了一個任務緩沖區隊列,所有被提交的任務(手動任務、調度任務)並不會直接被提交至集群,而是提交至這個緩沖區隊列中,然后固定數目的工作者線程再將緩沖區隊列的任務取出提交至集群執行。
 
這種運行模式在運行任務數不是很多的情況下尚且可行,一旦我們的常規分析需求變多,調度任務數也會隨之增加,這時臨時分析需求建立的手動任務就可能會影響調度任務的執行,這種影響主要表現在以下兩個方面:
 
(1)手動任務與調度任務共用一個隊列“hive”,而隊列“hive”的資源分配主要是根據調度任務的資源消耗情況進行分配的,這樣隨機提交的手動任務與調度任務共同執行時,就可能會影響原有調度任務的執行速度;而且無法根據兩種任務各自的特性選擇合適的調度策略(如:FIFO、Fair);
 
(2)手動任務的提交需要經過調度系統內部的緩沖區隊列,如果調度任務比較多,則手動任務可能需要在緩沖區隊列中等待很長的時間才能夠提交至集群運行,而任務運行時所需資源需要與調度任務共享,執行時間無法保證,用戶通常需要等待較長時間;
 
針對上述情況,我們對隊列“hive”進行拆分:hive.temporary、hive.common,其中hive.temporary用於臨時分析需求的手動任務,hive.common用於常規分析需求的調度任務,這樣我們就可以根據業務的實際情況,為兩者靈活的進行資源分配。
 
隊列拆分之后,我們就有了如下思考:手動任務(臨時分析需求)是否依然需要經過調度系統的緩沖區隊列再提交至集群?
 
隊列拆分一定程度上意味着資源的獨立,亦即手動任務與調度任務之間做到了物理上的隔離,如果手動任務依然使用調度系統的緩沖區隊列,則也必須是專門針對手動任務的隊列;而手動任務是臨時分析需求,不可能預留太多的資源給它,為了保證手動任務在可接受的時間內執行完成,我們必須限制隊列“hive.temporary”可同時運行任務的數目,以此來保證正在運行的任務有足夠的資源去執行;如果某個時間點提交給集群的手動任務超過隊列“hive.temporary”可同時運行任務數目的限制,則它們會被暫時“堆積”在Yarn調度器的隊列當中。
 
由此可見,我們並不需要再為手動任務在調度系統中建立專門的緩沖區隊列,直接利用Yarn調度器的隊列即可。我們可以直接將手動任務直接提交至集群,由Yarn調度器根據隊列“hive.temporary”根據調度策略及目前隊列資源使用情況決定是否運行任務。
 
因為現在的手動任務不經過調度系統的緩沖區隊列直接提交至集群,則手動任務的提交可以不再受限於Web系統,用戶可以通過Hive官方推薦的Beeline客戶端直接提交Hive SQL。
 
這就涉及到本文的核心問題:Beeline連接HiveServer2后如何使用指定的隊列(Yarn)運行Hive SQL語句?
 
解決方案
 
MapReduce運行隊列的指定是通過配置(Configuration)屬性“mapreduce.job.queuename”指定的。
 
大家可能首先想到的是通過“set mapreduce.job.queuename=queueName”的方式來選取運行隊列,這在手動任務(臨時分析需求)的場景下是不可取的,如前所述,我們為這類似的任務專門分配了相應的隊列資源“hive.temporary”,我們必須能夠保證用戶通過Beeline連接HiveServer2后提交的Hive SQL語句運行在指定的隊列“hive.temporary”中,而且用戶無法隨意更改運行隊列,即無法隨意更改屬性“mapreduce.job.queuename”。
 
目前HiveServer2使用的權限控制策略為SQL Standard Based Hive Authorization和Storage Based Authorization in the Metastore Server。其中SQL Standard Based Hive Authorization會對Hive終端命令“set”做出限制:只能針對白名單(hive.security.authorization.sqlstd.confwhitelist)中列出的屬性進行賦值。白名單默認包含一批屬性,其中就包括“mapreduce.job.queuename”,我們需要通過配置文件hive-site.xml或者啟動HiveServer2時通過參數“--hiveconf”設置白名單“hive.security.authorization.sqlstd.confwhitelist”的值,排除屬性“mapreduce.job.queuename”,使得我們的用戶通過Beeline連接至HiveServer2之后,無法隨意更改“mapreduce.job.queuename”的值。
 
既然用戶無法更改屬性“mapreduce.job.queuename”,那么HiveServer2啟動之后屬性“mapreduce.job.queuename”必須具有一個默認值,即“hive.temporary”,這樣用戶通過Beeline連接HiveServer2之后提交的Hive SQL就會運行在隊列“hive.temporary”中。那么,接下來的問題就是如果完成這個默認設定?
 
一般情況下,我們會這樣認為,HiveServer2的運行至少涉及到兩份配置文件:
 
(1)Hadoop:core-site.xml、hdfs-site.xml、mapred-site.xml、yarn-site.xml
 
(2)Hive:hive-site.xml
 
這些配置文件中的屬性值都會“打包”到MapReduce任務的配置屬性中去。我們自然會想到在mapred-site.xml或者hive-site.xml中指定“mapreduce.job.queuename”即可,然而實際驗證之后發現情況並不是這樣的。
 
(1)在hive-site.xml(mapred-site.xml)中指定“mapreduce.job.queuename”;
 
 
(2)測試指定值“test”是否生效;
 
 
我們發現通過Beeline使用賬號“hdfs”連接至HiveServer2后,屬性“mapreduce.job.queuename”的值被替換為“root.hdfs”,實際執行Hive SQL語句時也會發現相應的MapReduce任務被提交至隊列“root.hdfs”。
 
 
 
其中的一個解決方案如下:
 
 
其實質就是通過建立連接時的URL參數傳遞屬性值。
 
但是這種方法對於我們沒有用處,我們不可能強制我們的用戶連接HiveServer2時都指定隊列,但這個解決方案給了我們一個思路:既然我們可以通過“客戶端”建立連接時初始化屬性值,那么“服務端”接受連接時會不會也有初始化的操作?
 
通過對源代碼以及日志輸出的各種“DEBUG”,最終找到了問題所在。大家都知道,HiveServer2有一個“Session”的概念,就是每一個用戶有自己的一個會話(“set”的作用就是在用戶自己的會話中設置特定的屬性值),用戶會話之間相互隔離。根據我們的猜測,我們找到了HiveServer2 Session的實現類:org.apache.hive.service.cli.session.HiveSessionImpl.HiveSessionImpl,它的初始化(構造函數)就是“魔法”發生的地方,關鍵片段如下:
 
 
而且ConfVars.HIVE_SERVER2_MAP_FAIR_SCHEDULER_QUEUE的值默認為true,如下:
 
 
上述代碼片段默認情況下肯定會被執行,也就是說HiveSession的構建過程涉及到了Hadoop Scheduler的具體使用。
 
refreshDefaultQueue的作用就是在可用的情況下為特定用戶根據公平調度策略選取隊列,其中可用的情況:
 
(1)MapReduce2;
(2)用戶名不為空值;
(3)使用公平調度策略;
 
 
繼續跟進代碼,可以看到如下代碼片段:
…...
 
 
QueuePlacementPolicy assignAppToQueue的作用就是為用戶分配隊列的,其中YarnConfiguration.DEFAULT_QUEUE_NAME的值為default,MR2_JOB_QUEUE_PROPERTY的值為mapreduce.job.queuename,conf實際是HiveConf實例,我們在hive-site.xml或者mapred-site.xml中指定的mapreduce.job.queuename的值也就是在此處被覆蓋的(注意:每一個用戶會話中都保存着自己的conf)。
 
 
從上面的代碼可以看出,最終是由某個QueuePlacementRule決定用戶隊列的,那么什么決定rules的呢?
 
 
其中FairSchedulerConfiguration.USER_AS_DEFAULT_QUEUE的值為yarn.scheduler.fair.user-as-default-queue,FairSchedulerConfiguration.DEFAULT_USER_AS_DEFAULT_QUEUE的值為true,也就是說默認情況下rules包含兩個QueuePlacementRule實例:
 
(1)org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.QueuePlacementRule.Specified
 
 
如前所述,requestedQueue值為“default”,因此該rule返回值為“”(空字符串),繼續下一個rule的隊列選取。
 
(2)org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.QueuePlacementRule.User
 
 
該rule隊列的選取規則特別簡單:root. + 用戶名。
 
這也就是上述驗證過程中出現隊列“root.hdfs”的原因所在。
 
結論
 
綜上所述,用戶通過Beeline連接HiveServer2后的隊列選取,默認情況下受公平調度策略的影響,如果想通過hive-site.xml或者mapred-site.xml中指定mapreduce.job.queuename,有一個非常簡單的辦法就是將屬性值hive.server2.map.fair.scheduler.queue(ConfVars.HIVE_SERVER2_MAP_FAIR_SCHEDULER_QUEUE)置為false,可以在hive-site.xml中指定或或者啟動HiveServer2時通過參數指定,這樣HiveServer2隊列的選取就不再受公平調度策略的影響。
 
 
 
 
 
 
 
 
 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM