最近的工作是利用Hive做數據倉庫的ETL轉換,大致方式是將ETL轉換邏輯寫在一個hsql文件中,腳本當中都是簡單的SQL語句,不包含判斷、迴圈等存儲過程中才有的寫法,僅僅支持一些簡單的變數替換,比如當前賬期等。然後通過一個通用的shell腳本來執行hsql文件。該腳本是主要是調用了hive -f ...
最近的工作是利用Hive做數據倉庫的ETL轉換,大致方式是將ETL轉換邏輯寫在一個hsql文件中,腳本當中都是簡單的SQL語句,不包含判斷、迴圈等存儲過程中才有的寫法,僅僅支持一些簡單的變數替換,比如當前賬期等。然後通過一個通用的shell腳本來執行hsql文件。該腳本是主要是調用了hive -f <hsql文件>來執行hsql文件中的SQL語句的,當然hive命令會通過--hivevar選項定義變數將當前賬期等數值傳進去供SQL使用。
簡單說下環境信息,目前使用的大數據平臺版本是HDP 3.1.0.0-78,Hive版本是3.1.0,而Tez版本是0.9.1。Hive 3.X系列的新特性主要包括:
1. 執行引擎不再支持mr,取而代之的是tez或者spark(在HDP平臺預設是tez);
2. 不再支持胖客戶端Hive CLI,被beeline取代(目前通過hive命令執行sql實際還是調用的beeline去連接hiveserver2服務);
3. 預設建表支持ACID語義;
4. 支持LLAP,即Live Long and Process,相當於記憶體計算,極大地優化了性能(該特性實際從Hive 2.X開始支持);
言歸正傳,回到本文的主題,比如Hive在運行過程中報錯了,我們需要在Yarn上找到對應的application的日誌以便定位問題,前提是需要知道Yarn程式對應的applicationId,但是beeline的輸出信息中是沒有applicationId的,那麼如何找到Hive提交的SQL相對應的Yarn程式的applicationId呢?主要有以下幾個步驟:
1. 我們通過shell腳本提交hsql文件時實際是通過beeline向hiveserver2服務提交hsql文件中的SQL語句,我們的shell腳本會將beeline的屏幕輸出信息同時重定向到日誌文件中,這個就是我們的第一個步驟的日誌。我們找到這個日誌文件,在其中搜索關鍵字"Completed executing command",可以得到queryId,其中每個SQL語句對應1個queryId,因為我們的hsql腳本中有4個SQL語句,所以搜索出來的信息如下:
INFO : Completed executing command(queryId=hive_20200502095437_1e9bf52d-e590-4519-a6e1-9e2e4ae91158); Time taken: 0.755 seconds
INFO : Completed executing command(queryId=hive_20200502095816_888a7dba-4403-439d-a3a7-f6cdc280c18a); Time taken: 52.929 seconds
INFO : Completed executing command(queryId=hive_20200502100121_5752f019-a6e2-463c-b413-a80bbe518a5c); Time taken: 52.66 seconds
INFO : Completed executing command(queryId=hive_20200502100428_9d4b8955-b84c-40be-a605-9ce19b4b7773); Time taken: 26.463 seconds
2. 找到對應的hiveserver2服務在哪台機器上。由於beeline是通過zookeeper隨機連接一個hiveserver2服務,所以從上一步的日誌中可以看到連接的是哪台機器上的hiveserver2服務。然後登錄到該台主機,通過netstat和ps命令找到對應的hiveserver2進程,從ps命令輸出的進程信息對應的命令行中,我們可以找到下麵的參數。
-Dhive.log.dir=/var/log/hive -Dhive.log.file=hiveserver2.log
上面的參數說明瞭hiveserver2服務對應的日誌名稱和路徑。這樣我們就可以找到hiveserver2服務對應的日誌,這是我們第二個步驟的日誌。從這個日誌里通過搜索關鍵字"callerId=<queryId>",<queryId>用上一步得到的真實的queryId替換(比如搜索"callerId=hive_20200502095816_888a7dba-4403-439d-a3a7-f6cdc280c18a")。我們將上面的4個queryId逐一用前述的關鍵字搜索,得到信息如下:
2020-05-02T10:00:30,440 INFO [Thread-536694]: client.TezClient (:()) - Submitting dag to TezSession, sessionName=HIVE-315802e2-f6e4-499d-a707-4d3057180abd, applicationId=application_1588062934554_53656, dagName=create temporary table ngdwt.rpt_to_etc_...t (Stage-1), callerContext={ context=HIVE, callerType=HIVE_QUERY_ID, callerId=hive_20200502095816_888a7dba-4403-439d-a3a7-f6cdc280c18a }
2020-05-02T10:00:57,229 INFO [Thread-536729]: client.TezClient (:()) - Submitting dag to TezSession, sessionName=HIVE-315802e2-f6e4-499d-a707-4d3057180abd, applicationId=application_1588062934554_53656, dagName=create temporary table ngdwt.rpt_to_etc_...t (Stage-4), callerContext={ context=HIVE, callerType=HIVE_QUERY_ID, callerId=hive_20200502095816_888a7dba-4403-439d-a3a7-f6cdc280c18a }
2020-05-02T10:03:22,043 INFO [Thread-537002]: client.TezClient (:()) - Submitting dag to TezSession, sessionName=HIVE-315802e2-f6e4-499d-a707-4d3057180abd, applicationId=application_1588062934554_53656, dagName=create temporary tabl...ov_in,a.statis_date (Stage-1), callerContext={ context=HIVE, callerType=HIVE_QUERY_ID, callerId=hive_20200502100121_5752f019-a6e2-463c-b413-a80bbe518a5c }
2020-05-02T10:07:39,627 INFO [Thread-537495]: client.TezClient (:()) - Submitting dag to TezSession, sessionName=HIVE-315802e2-f6e4-499d-a707-4d3057180abd, applicationId=application_1588062934554_53656, dagName=insert into ngdwt.rpt_to_etc_rece_d(re...t (Stage-1), callerContext={ context=HIVE, callerType=HIVE_QUERY_ID, callerId=hive_20200502100428_9d4b8955-b84c-40be-a605-9ce19b4b7773 }
可以看到第1個queryId用關鍵字"callerId=<queryId>"去搜索沒有搜到信息,因為對應的第一個sql語句是ddl語句,不會向yarn提交程式(但是僅用"<queryId>"去搜索還是能搜到信息),後面第二個queryId搜索出來有2行,其他queryId只有1行。
可以看到這些queryId(hive命令輸出信息)或者callerId(hiveserver2.log日誌信息)對應的hive session和yarn application是同一個:
sessionName=HIVE-315802e2-f6e4-499d-a707-4d3057180abd
applicationId=application_1588062934554_53656
也就是說,同一個hsql文件中的不同SQL語句對應的是同一個hive session以及同一個yarn application.
來看下yarn web管理頁面中該application的截圖。
可以看到上面頁面中的Name跟hiveserver2.log中的sessionName一致,Application Tags跟hiveserver2.log中的callerId(或者hive命令屏幕輸出信息中的queryId)一致。
而yarn ui2中的程式信息如下:
3. 找到了applicationId就比較好辦了,可以通過下麵的命令將yarn日誌從hdfs下載到本地(待yarn程式執行完畢)
yarn logs -applicationId application_1588062934554_53656 > application_1588062934554_53656.log
然後可以對application_1588062934554_53656.log做進一步的分析。
比如用關鍵字"Container: container_"搜索並去重排序後得到9個container的信息:
Container: container_e46_1588062934554_53656_01_000001 on hadoop19_45454_1588385301489
Container: container_e46_1588062934554_53656_01_000002 on hadoop31_45454_1588385300882
Container: container_e46_1588062934554_53656_01_000003 on hadoop40_45454_1588385301059
Container: container_e46_1588062934554_53656_01_000004 on hadoop27_45454_1588385300739
Container: container_e46_1588062934554_53656_01_000006 on hadoop36_45454_1588385301268
Container: container_e46_1588062934554_53656_01_000007 on hadoop57_45454_1588385301076
Container: container_e46_1588062934554_53656_01_000008 on hadoop22_45454_1588385301501
Container: container_e46_1588062934554_53656_01_000009 on hadoop31_45454_1588385300882
Container: container_e46_1588062934554_53656_01_000010 on hadoop21_45454_1588385301473
上面是該application對用的所有container.
或者用關鍵字"Assigning container to task:"搜索得到任務分配信息,其中container_e46_1588062934554_53656_01_000001因為是applicationmaster沒有任務分配信息,其他8個container都有任務分配信息,其中container_e46_1588062934554_53656_01_000008和container_e46_1588062934554_53656_01_000009有2條記錄,但attempt不同,表示這2個container里的任務之前有失敗的,分別進行了2次嘗試。為了簡潔起見,這裡僅列出搜索出來的第一條記錄:
2020-05-01 22:00:40,603 [INFO] [DelayedContainerManager] |rm.YarnTaskSchedulerService|: Assigning container to task: containerId=container_e46_1588062934554_53656_01_000002, task=attempt_1588062934554_53656_1_00_000000_0, containerHost=hadoop31:45454, containerPriority= 11, containerResources=<memory:12288, vCores:1>, localityMatchType=RackLocal, matchedLocation=/default-rack, honorLocalityFlags=false, reusedContainer=false, delayedContainers=3
因為現在的Hive的執行引擎不再是mr,而是改成了tez,目前我對tez並不太熟悉,只是理解它為mr的升級版,在原來的map/reduce操作上增加了DAG,不同job之間的數據傳遞不必寫到HDFS,而是類似數據流的方式,減少了中間環節,提升了效率。對於一個Tez程式,類似於MR程式的MRAppMaster和YarnChild進程,它會產生DAGAppMaster和TezChild進程,前者是master負責管理整個程式以及申請資源,後者是slave,負責執行具體的計算任務。