Hive0.13到Hive2.1跨版本升級全姿勢

作者:餓了么數據架構組

Hive是業界大數據平台使用最廣泛的SQL引擎,提供了一層SQL抽象介面和一套元數據規範, 將SQL查詢翻譯為分散式的計算作業,支持MapReduce/Spark/Tez等多種計算引擎。 同時Hive定義的元數據標準已經成為了一種事實標準,業界流行的大數據SQL引擎均對Hive元數據進行了兼容和支持。

前一段時間我們餓了么數據架構團隊對Hive進行了一次從0.13版本到2.1版本的跨版本升級,升級期間遇到了一些問題, 但是基本做到了可灰度可控制和升級期間穩定性保證,同時服務不停這個屬性通過我們的分析和實踐也可以達到。

本文詳細介紹一下我們在升級過程前後遇到的一些問題和解決思路。以供大家參考。

平台使用背景:

  1. 深度使用Hive的各項服務。
  2. 生產環境每天SQL查詢總量80000+,包含 各種正常的和奇葩的SQL語法和元數據存儲。
  3. 同時使用Spark和Hive進行ETL,使用Presto、Kylin和Spark進行Adhoc查詢加速。

下面從元數據、語法兼容性、Hive2.1新功能、UDF兼容性、Hive2.1的一些不足等幾個方面, 來討論下升級注意事項和升級期間踩過的一些坑。

1. 元數據

Hive元數據是Hadoop平台的核心數據,如果只是使用Hive提供的Hive Schema Tools來進行元數據Schema升級的話,整個升級過程是黑盒,並不利於精確控制,在深度使用場景下還有可能爆各種奇葩錯誤。

例如如果你同時在用Spark,那麼元數據VERSION表中的版本可能被修改為1.2.1(實際版本是0.13),如果 使用HiveSchemaMetaTools時候參數不注意設定,這貨是會從1.2.1版本開始執行升級腳本,最後會一部分升級腳本成功,一部分失敗,然後結果你懂得。

所以首先需要對Hive元數據從0.13到2.1的Schema升級腳本進行詳細分析,再確定具體升級方式。

0.13到2.1的元數據Schema升級腳本,位於

${HIVE_HOME}/metastore/scripts/upgrade/${DB_TYPE}/

文件夾內。一共17個子升級腳本和對應的issue,通過查看每一個腳本明細SQL、對應的issue信息以及適用場景,可以將Schema升級腳本分為以下幾類:

1.1建議升級:hive-13076(涉及到建表和修改表操作)

從代碼分析上看不影響Hive正常操作,但是修改內容涉及到建表和修改表操作,另外這個腳本的升級內容是一個建表操作,不涉及到修改資料庫表操作,所以風險較小,建議在灰度2.1客戶端之前先進行升級操作。

這個升級目的是讓hive支持表級別主鍵和外鍵,從而可以在CBO時候優化join的執行計劃。 目前社區完成了主鍵和外鍵的語法支持和存儲,而CBO使用主鍵和外鍵功能還處理未開發狀態。

Hive2.1支持的新語法:

CREATE TABLE vendor( vendor_id int, PRIMARY KEY (vendor_id) DISABLE NOVALIDATE RELY); CREATE TABLE product ( product_id int, product_vendor_id int, PRIMARY KEY (product_id) DISABLE NOVALIDATE, CONSTRAINT product_fk_1 FOREIGN KEY (product_vendor_id) REFERENCES vendor(vendor_id) DISABLE NOVALIDATE );ALTER TABLE table2 ADD CONSTRAINT pkt2 primary key (a) disable novalidate; ALTER TABLE table3 ADD CONSTRAINT fk1 FOREIGN KEY ( x ) REFERENCES table2(a) DISABLE NOVALIDATE RELY; ALTER TABLE table6 ADD CONSTRAINT fk4 FOREIGN KEY ( y ) REFERENCES table1(a) DISABLE NOVALIDATE;

1.2 事務(Hive Transaction)相關修改(不啟用Hive事務則不需要升級)

默認事務是不啟用的,當通過配置啟用Hive事務時候使用到。Hive事務的應用場景比較有限。

5. hive-12807 6. hive-12814 7. hive-12816 8. hive-12818 9. hive-12819 10. hive-12821 11. hive-12822 12. hive-12823 13. hive-12831 14. hive-12832 16. hive-13395 17. hive-13354

1.3 Hcatalog相關修改(不使用Hcatalog則不需要升級)

增加NOTIFICATION_LOGNOTIFICATION_SEQUENCE表。支持hcatalog metastore notification機制。

2. hive-9296

1.4 其他非關鍵修改(一些特殊場景使用到)

1. hive-7784: 給`PART_COL_STATS`表增加索引,加快CBO查錶速度。3. hive-7018: 刪除`TBLS`和`PARTITIONS`表在0.10後不用的`LINK_TARGET_ID`列。 0.13版本schema本身就不含有這個列。Oracle可能會遇到這個問題。不建議執行,因為這個操作危險性比較大。4. hive-11970: 增加`COLUMNS_V2`等表的`COLUMN_NAME`列名長度到767, 防止某些特殊情況例如Avro導出表情況下列名超過128個字元。一般情況遇不到

總結

通過上述分類分析可以知道,0.13到2.1版本的元數據基本完全兼容,根據公司具體場景進行部分升級即可。 例如,如果不使用以上提到的Hive事務、Hcatalog、Avro導出等特殊功能,完全可以做到只升級Hive相關程序不升級元數據也沒有使用問題。 但是需要做以下前提工作:

  1. 關閉元數據VERISON校驗相關功能:設置hive.metastore.schema.verification=false。必要時候修改代碼,禁用Hive自身的Check機制。
  2. 配置禁止JDO框架來自動更新元數據schema:設置datanucleus.fixedDatastore=truedatanucleus.autoCreateSchema=false

2. 語法兼容性

Hive版本升級後,給人的感覺是語法要求越來越嚴格,越來越接近於ANSI SQL標準,所以很多在0.13可以跑成功的SQL,到了2.1會報錯。 下面總結升級過程中遇到的一些語法兼容性問題。

2.1 Hive新增保留字問題

Hive隨著版本變遷,保留字越來越多。 保留字的詳細情況可以參考:Hive各個版本的保留字

保留字問題有以下兩種:

2.1.1 新增預留字

新的版本Hive會預留更多保留字,如果SQL用到產生語法問題,可以通過參數設置 set hive.support.sql11.reserved.keywords=false來取消保留字校驗, 達到不需要修改SQL,又能保證線上SQL穩定運行的兼容效果。

2.1.2 新增關鍵字

當SQL中含有Hive2.1中新增的關鍵字時候,只能修改SQL進行兼容了,需要將與關鍵字衝突的屬性名用反引號包圍。 新版本增加的關鍵字有:OFFSET、SUBQUERY、REWRITE、PRIMARY、FOREIGN、KEY、 REFERENCES、CONSTRAINT等。

2.2 其他兼容性問題和解決方法

  • hive.開頭的參數如果不在Hive2.1預定義配置中(可能已經被移除),或者大小寫不對,set語句會直接報錯。

例如:set hive.auto.convert.JOIN=true,大小寫不對,執行會報錯。解決方法:規範化語句,或者關閉強制校驗參數。

  • 建表時候不指定列的具體類型,在2.1會直接報錯。

例如:

create table test_table_null_no_type as select null as id, "zhangsan" as name;

解決方法:規範化SQL,指定具體的列類型

  • 通過函數等運算產生的列,沒有指定明確的別名,在2.1會偶爾報錯。

例如:

select id, `_c1` from (select id, get_json_object(deliver_geojson,$.features) from tb) a

解決方法:規範化SQL,指定有業務含義的別名,不以下劃線開頭

  • 在使用windows函數的時候,當列名在多個表都存在的時候,不指定列所屬表,在2.1會報錯。

例如:

select row_number() over(partition by user_id order by a.log_time desc) rank from a join b on a.id = b.id;

其中user_id在a表和b表中均存在。

解決方法:規範化SQL,指定列所屬表名。

  • 在使用union all的時候,union起來的多個查詢,含有orderBy、clusterBy、distributeBy sortBy、limit語法在2.1會報錯。

例如:

select 1 from default.dual limit 1 union all select 2 from default.dual limit 1;

解決方法:只允許在最後一個語句中含有orderBy、clusterBy、distributeBy sortBy、limit語句。

  • 使用windows函數時候,窗口的上限和下限必須大於0,否則在2.1會報錯。

--錯誤信息:SemanticException Window Frame Boundary Amount must be a positive integer, provided amount is: 0 select order_date ,order_date_new ,restaurant_id ,phone_1 ,count(phone_1) over(PARTITION BY restaurant_id,phone_1 order by days range between 0 preceding and 30 following) buy_num Hive Schema Tools from tb01;

解決方法:規範化SQL,避免窗口函數上限和下限為0,例如將0 preceding修改為current row

  • case when語法中的數據類型不一致,在2.1會報錯

解決方法:將case when里類型轉換為一致。

2.3 構建大數據SQL預上線測試環境

語法兼容性問題,可以提前拿到線上一段時間的SQL語句,啟動一個2.1版本的HiveServer2, 通過批量Explain去校驗是否存在語法兼容性問題,從而把所有問題解決到事前。 我們這邊的做法是構建一個通用的大數據SQL測試環境,具體做法是:

  1. 數據集和Workload

通過客戶端自定義的Hook和重放MapReduceLog/SparkEventlog等方法,拿到生產環境所有的SQL語句。

數據集合使用線上數據,事實一再證明,生產數據+生產SQL才能完全表徵一個生產環境。

將數據輸出關鍵字(Create Table/Insert into)統一修改為指向測試庫表。

按需提取所需要的SQL,例如全集、按照特徵提取採樣、Adhoc、生產ETL等維度來提取。

  1. 一鍵化測試

可以對比性能、數據質量、執行計劃和錯誤分類等。融合成一個Hive/Spark/Presto新功能上線前的回歸測試工具來使用。

3. Hive2.1新feature

3.1 ORC&Parquet文件格式

0.13對於2.1產生的ORC文件,存在讀取兼容性問題。 另外hive2.xx之前,ORCInputformat存在一個UGI錯誤的bug比較嚴重,在HiveServer2里會遇到, 原因是ORCReader使用了多線程載入ORC文件的footer,而UGI的繼承是不能跨線程的。

另外hive2.xx之前,Parquet也存在諸多的bug,可以參考社區相關的issue。

在文件格式的選擇上,目前可參考的範例有:

  • Uber: Parquert + snappy
  • Linkedin: ORC + zlib
  • Didi: ORC + zlib
  • GrowingIO: ORC + zlib

我們傾向於主要文件格式選擇ORC,因為ORC和Parquet測試下來速度差不多。 但是Spark對於Parquet的一些加速特性需要DataSource API,因為不支持混合文件格式表而被我們關閉。而Hive/Presto對於ORC的支持更友好,例如讀取速度、向量化、快速合併和統計信息優化等。

至於壓縮演算法的選擇,我們傾向於對不同場景選擇不同的壓縮演算法, 例如對數倉的ODS層,數據量很大,使用頻率很低,考慮使用zlib壓縮演算法,達到最高壓縮比。 而DM層數據,數據量相對小但是訪問頻率高,則考慮使用snappy壓縮演算法,在壓縮比和解壓縮速度之間取得一個tradeoff。 目前文件格式和壓縮演算法的選擇正在逐步推廣上線階段。 我們的推廣方法是改表格式+生命周期,使得數據從之前的格式逐漸滾動到ORC格式。

3.2 CBO

目前我們還在測試階段。原因是CBO會在某一些生產SQL語句解析時候報錯Assert Error。 另外CBO的適用場景主要在於Join順序的選擇上,這個還需要在自己的線上場景上進行測試一番。

3.3 向量化執行

Hive的向量化執行目前只支持ORC文件格式,Parquet支持正在開發。在和文件格式推廣同步測試和推廣階段。

3.4 Hive On Spark/Tez

On Spark目前看來社區還不成熟,目前發現只有Uber等在維護和使用。

Tez目前更加冷清,之前用過一些效果還是很不錯的,但是一直沒有火起來。

我們的方法是逐漸推廣SparkSQL加速部分的ETL語句和Adhoc查詢,並計劃作為未來的主執行引擎,目前我們已經把SparkSQL 對原來HiveSQL的語法兼容性運行成功率做到92%+,這樣讓我們的線上ETL SQL可以更加平滑低成本進行遷移。

我們在Spark上進行的一些工作會有專門文章進行詳細介紹。

3.5 其他

Hive2.1提供了大量的bug修復。除了上面提到的還有:

  • HiveServer2的Heap和PermGen內存泄露問題。
  • 大量存儲類型bug修復。參考社區的相關的issue list。

4. UDF兼容性

  • trim函數在2.1不支持string之外的類型。

0.13支持,但是2.1不支持,想要兼容的拷貝0.13代碼就可以解決。

  • date_add和date_sub函數2.1和0.13返回的類型不一致

2.1之前返回String類型,2.1之後返回date類型。如果where語句中有data_add函數與String比較,可能導致數據查詢不出來。 想要兼容的話,可以回滾到0.13版本的date_add和date_sub代碼。具體請參考HiveUDF Date Functions

5. Hive2.1存在的一些問題

5.1 Hive Schema Version問題

如果元數據與Hive版本不同步升級,或用到Spark,因為Spark依賴的Hive默認是1.2.1。所以元數據也中的VERSION表, 會被改來改去,導致各種報錯,例如:MetaException(message:Hive Schema version 2.1.0 does not match metastores schema version 1.2.0 Metastore is not upgraded or corrupt)

一種方法是設置hive.metastore.schema.verification=false。

還有一種徹底的方法是把Hive啟動時候檢查Schema的功能屏蔽掉。

5.2 Metastore Server內存泄露問題

BoneCP+DirectSql開啟時候Metastore Server內存泄露問題, 參考HIVE-15551,這個issue在2.2才完全解決,在Metastore Server端長期運行可能遇到,可以提前打patch來解決。

5.3 HiveServer2的多用戶模擬問題

2.1後的HS2模擬多用戶代碼里,UGI的impersonation方式從CreateRemoteUser變為CreateProxyUser。

好處是服務端可以獲取到代理用戶和被代理用戶的信息,缺點是這種機制需要在Namenode端為每個被代理用戶進行配置。 具體請參考:Hadoop Impersonation

如果不想這麼麻煩,可以從CreateProxyUser方式回滾到CreateRemoteUser的方式,具體實現可以參考Hive1.2.1相關代碼實現。

5.4 HiveServer2的operationlog不列印到客戶端問題

參考HIVE-14183,設置hive.async.log.enabled=false來解決。

5.5 Hive客戶端PermGen OOM的問題

hive-env.sh設置export HADOOP_OPTS="$HADOOP_OPTS -XX:MaxPermSize=128m"解決。

5.6 HiveServer2的性能問題

hive.driver.parallel.compilation參數默認為false,導致HS2隻允許同時一個Query編譯, 有操作元數據比較多的查詢編譯讀取元數據會比較慢,全局鎖會卡住所有其他查詢。 需要設置為false,打開允許多個Query同時編譯。

5.7 基於Database的Stats收集策略被棄用

Hive2.1的Stats收集只能使用基於HDFS的策略,而StatsTask是單線程運行讀取HDFS上的統計文件(文件數量等於Mapper數量),因為HDFS抖動導致了很多性能問題。 極端情況下,發現過一個statsTask會執行1個小時之久。

我們正在考慮把Stats Task禁掉,或者切換回基於Database的Stats收集策略。

5.8 Column Pruning時候導致列順序錯誤問題

Column Pruning時候導致列順序錯誤,造成處理時候ArrayIndexOutOfBoundsException。 在一些複雜的SQL里增加limit會發生,參考HIVE-14564來解決。

5.9 我們Team回饋社區的一些patch

Alter table Cascade時候的NPE問題:HIVE-16877

Drop掉分區後執行insert overwrite報錯問題(這個問題比較嚴重,有可能不報錯,但是會引入臟數據):HIVE-17063

非當前庫內Alter partition的異常問題:HIVE-17309

6. 其他一些踩過的坑

6.1 元數據資料庫連接數打滿問題

元數據直連資料庫方式下,當並發服務量非常巨大時候,MySQL默認連接數是4000,比較容易打滿。

解決方法:

  • 直連使用BoneCP連接池方式下,maxConnectionsPerPartition默認值為10,一個客戶端會建立20個連接,可以降低為2,並且降低連接池的活躍度。 可以考慮在$HIVE_CONF_DIR下增加一個bonecp-config.xml:

<?xml version="1.0" encoding="UTF-8"?> <bonecp-config> <default-config> <property name="maxConnectionAgeInSeconds">5</property> <property name="acquireIncrement">1</property> <property name="maxConnectionsPerPartition">2</property> <property name="lazyInit">true</property> </default-config> </bonecp-config>

  • 提升資料庫連接數最大值,可能會有穩定性風險。
  • 使用Metastore Server,這是比較通用的做法,一個Metastore Server扛10000+個客戶端連接,後端400個資料庫連接毫無壓力。

Metastore Server生產化實踐可以參考我們的另一篇文章。請Google搜索Hive Metastore Server生產化實踐。

7. 總結

7.1 升級流程

總結之前的分析,從0.13升級到2.1版本,升級過程可以達到完全灰度和平滑

具體的升級流程如下:

  1. 元數據schema備份。
  2. 提前批量校驗線上SQL,解決語法兼容性問題。
  3. 元數據只升級hive-13076中的腳本,建立KEY_CONSTRAINTS表和表上的索引。
  4. 定製化Hive代碼,根據使用場景解決2.1裡面存在的UDF和其他的一些問題。
  5. 開始灰度升級Hive 2.1客戶端,HiveServer2,最後是Metastore Server。
  6. Hive2.1客戶端穩定後,根據需要部分或者全量來將元數據schema升級到2.1.1版本。
  7. 最後依次升級Hive JDBC連接客戶端。(非必需)

7.2 未來計劃

未來對於Hive On MapReduce執行慢的問題,我們計劃逐漸把主要SQL引擎切換SparkSQL,Hive只承擔兜底降級的角色。

同時我們計劃實現一個統一的大數據平台SQL引擎,在計算引擎層,合併Kylin、Presto、SparkSQL和Hive等幾種引擎, 提供一個統一的路由層,發揮各個引擎的最佳性能; 在數據緩存層,結合SQL畫像和歷史執行情況,緩存中間熱表數據,自動建立Kylin Cube。 在數據存儲層,引入CarbonData文件格式,做數據層索引加速。 同時由於Hive定義的元數據規範已經成為大數據平台的事實標準,我們會繼續沿用這種標準,所以在和社區一起推動CarbonData和Hive元數據的兼容,歡迎關注CarbonData。

目前正在開發階段,敬請期待後繼的介紹文章。

8. 附錄

8.1 Hive JDBC客戶端和HiveServer2各版本之間兼容性

8.2 從0.13開始的元數據Schema升級明細和影響分析

github gist鏈接


推薦閱讀:

大數據必備知識:數據的分類方式
從頭學習大數據培訓課程 數據倉儲工具 hive(四)hive 的 select、union、SQL 依賴並發執行
[英語句翻6-10]讓英語學習不再痛苦

TAG:Hive | 大數據處理 |