場景描述
hive 數(shù)據(jù)表的導(dǎo)入導(dǎo)出功能部分代碼如下所示,使用 assemble 將 Java 程序和 spark 相關(guān)依賴一起打成 jar 包,最后 spark-submit 提交 jar 到集群執(zhí)行。
public class SparkHiveApplication {
public static void main(String[] args){
long start = System.currentTimeMillis();
String writeSql = "";
SparkConf sparkConf = new SparkConf();
for (String arg : args) {
if (arg.startsWith("WriteSql=")) {
writeSql = arg.replaceFirst("WriteSql=", "");
}
}
SparkSession spark = SparkSession
.builder()
.appName("write data to hive table")
.config(sparkConf)
.enableHiveSupport()
.getOrCreate();
// LOAD DATA LOCAL INPATH '/path/to/file.csv' INTO TABLE target_table PARTITION (field='x')
spark.sql(writeSql);
long end = System.currentTimeMillis();
System.out.println("cost time:" + (end - start));
}
}
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.11</artifactId>
<version>2.4.8</version>
</dependency>
在CDH6.3.2 集群(后面稱CDH),當(dāng)程序執(zhí)行 spark.sql 導(dǎo)入本地磁盤 csv 數(shù)據(jù)到 hive 表時出現(xiàn)異常(如下),但導(dǎo)出表數(shù)據(jù)到本地磁盤、從 HDFS 導(dǎo)入導(dǎo)出功能卻都是正常的。
Caused by: java.lang.IllegalArgumentException: Wrong FS: file:/input/data/training/csv_test1_1301125633652294217_1690451941587.csv, expected: hdfs://nameservice1
at org.apache.hadoop.fs.FileSystem.checkPath(FileSystem.java:649)
查資料判定是 spark-hive_2.11 版本不兼容導(dǎo)致的,在調(diào)試的過程中陸續(xù)又出現(xiàn)異常(如下)
Exception in thread "main" org.apache.spark.sql.AnalysisException: org.apache.hadoop.hive.ql.metadata.HiveException: Unable to fetch table csv_test2. Invalid method name: 'get_table_req';
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/hadoop/hive/ql/metadata/HiveException
at java.lang.Class.getDeclaredConstructors0(Native Method)
最終使用 spark-hive_2.1.1: 2.4.0-cdh6.3.3 解決了最初的本地磁盤導(dǎo)入異常。
接著用包含 spark-hive_2.1.1: 2.4.0-cdh6.3.3 依賴的 jar 包在 CDP 集群(另一個大數(shù)據(jù)集群)執(zhí)行導(dǎo)入導(dǎo)出時又拋了異常,修改依賴版本為 spark-hive_2.11: 2.4.8 ,異常解決。
java.lang.NoSuchMethodException: org.apache.hadoop.hive.ql.metadata.Hive.alterTable(java.lang.String, org.apache.hadoop.hive.ql.metadata.Table, org.apache.hadoop.hive.metastore.api.EnvironmentContext)
此時兩個集群中參與導(dǎo)入導(dǎo)出的部分組件版本如下:
集群 | spark | hive | Java 中的 spark-hive_2.1 |
---|---|---|---|
CDH | 3.0.x | 2.1.1 | 2.4.0-cdh6.3.3 |
CDP | 3.0.x | 3.1.3 | 2.4.8 |
備注:導(dǎo)入導(dǎo)出操作采用 spark on k8s 方式執(zhí)行,所以使用是鏡像中的 spark 3.0 而非 CDH 、CDP 集群上安裝的 spark。
異常原因分析
spark.sql 執(zhí)行時要做三件事情:
- spark 首先創(chuàng)建 hiveMetaStoreClient 對象;
- 再調(diào)用 hiveMetaStoreClient 的方法去跟 CDH(CDP) 中的 hiveMetastoreServer 通信獲取表相關(guān)元信息。
- 根據(jù)獲取到的信息生成 sql 的執(zhí)行計劃,真正處理數(shù)據(jù)。
生成對象 jvm 首先需要通過全限定類名找到對應(yīng) Class 文件,通過反射的方式構(gòu)造出對象再執(zhí)行對象方法。問題也在這個地方:包名+類名相同,不同版本可能方法名、方法參數(shù)、方法內(nèi)容不同,對應(yīng)的出現(xiàn) Invalid method name: 'get_table_req'
、 java.lang.NoSuchMethodException
以及方法執(zhí)行時拋出異常。
場景描述中更換依賴版本實際上是在找適配的 hiveMetastore 版本,并且讓 jvm 率先加載到。2.4.0-cdh6.3.3 內(nèi)部包含 hive-metastore:2.1.1-cdh6.3.3,2.4.8內(nèi)部包含 hive-metastore:1.2.1spark2。
另一種解決方式
spark1.4.0 以后的版本支持和不同版本的 Hive Metastore 交互。列表貼的是 spark 3.4.1 兼容的 hive meatstore 版本 0.12.0 到 2.3.9 和 3.0.0 到 3.1.3。不同版本兼容可在官方文檔查看。
怎么配置和不同版本 hive metastore 交互?
(1)內(nèi)置。spark 內(nèi)置了 hive,如果應(yīng)用程序 jar 包中也沒有帶,也沒有外部指定時,默認(rèn)使用內(nèi)置的。不同版本 spark 內(nèi)置的 hive 版本也有差異,spark3.4.1 內(nèi)置 hive2.3.9,spark3.0.3 內(nèi)置 hive2.3.7。在 spark-shell 中使用 spark.sql 時應(yīng)該用的是內(nèi)置的,因為那會沒有 Java jar 包,啟動也僅僅是在命令行敲了“spark-shell”。
(2)當(dāng)場下載。配置spark.sql.hive.metastore.version=2.1.1
和 spark.sql.hive.metastore.jars=maven
,當(dāng)執(zhí)行spark.sql 時會先從 maven 倉庫下載 2.1.1 相關(guān)的依賴到本地 /root/.livy/jars 路徑下,大概 188 個 jar 包,總大小 200M 左右。但這種方式當(dāng)網(wǎng)速很慢或者 maven 倉庫沒有某些依賴時會下載失敗,而且當(dāng)場下載也不適合生產(chǎn)環(huán)境。
(3)指定版本以及依賴的路徑。
- spark 3.1.0 之前配置
spark.sql.hive.metastore.version=2.1.1
且spark.sql.hive.metastore.jars=/path-to-hive-jars/*
。執(zhí)行 spark.sql 時就會率先從 path-to-hive-jars 路徑下尋找依賴。 - spark 3.1.0 之后需要配置
spark.sql.hive.metastore.version=2.1.1
、spark.sql.hive.metastore.jars=path
、spark.sql.hive.metastore.jars.path=path-to-hive-jars
?!皃ath-to-hive-jars” 可以是 HDFS 上的路徑,具體細(xì)節(jié)看表格介紹。
? 這種方式可以用在生產(chǎn)環(huán)境中。
如果采用方式(3)怎么提前獲取到正確的依賴,既能跟 spark 兼容又能和集群 hive 通信沒問題?
要操作哪個集群如果該集群 hive 在 spark 版本兼容的范圍內(nèi)。直接將集群 hive/lib 下的全部 jar 包(200M左右)“懟” 給 spark 就可以了。(可能用不了那么多,但篩選需要做實驗測試)。
下面是在 CDH 集群執(zhí)行導(dǎo)入操作時的 spark-submit 命令。提前將 CDH 的 hive/lib 下的 jar 包拿出來掛載到容器的 /opt/ml/input/data/training/sparkjar/hive-jars 路徑下。
# 在 k8s 容器中執(zhí)行
/usr/local/spark/bin/spark-submit \
--conf spark.driver.bindAddress=172.16.0.44 \
--deploy-mode client \
--conf spark.sql.hive.metastore.jars=/data/training/sparkjar/hive-jars/* \
--conf spark.sql.hive.metastore.version=2.1.1 \
--properties-file /opt/spark/conf/spark.properties \
--class com.spark.SparkHiveApplication \
local:///data/training/sparkjar/hive-metastore-spark-app-jar-with-dependencies.jar \
WriteSql=TE9BRCBEQVRBIExPQ0FMIElOUEFUSCAnL29wdC9tbC9vdXRwdXQvMTc1NjQ2NDY2MDY3Mzk4NjU3LzE3NTY0NjQ2NjA2NzM5ODY1Ny9wYXJ0LTAwMDAwLWVhYjA2ZWZiLTcwNTktNGI4MS04YmRhLWE3NGE5Yzg3OTY2MS1jMDAwLmNzdicgSU5UTyBUQUJMRSBkdF90aW9uZV90ZXN0XzIwMjIwNzIyIHBhcnRpdGlvbiAocGFydF9udW09JzEnKQ==
與工程結(jié)合時肯定能獲取到全部 jar 包以及找到合適的“懟”方式。這里列舉的只是一種向 spark 任務(wù)添加依賴的方式。
嘗試打“瘦”包
在創(chuàng)建 assembly jar 的時候,將 spark-hive_2.1 的生命周期設(shè)置為 provided,即不將該依賴打入最后的 jar 包。因為在運(yùn)行 jar 任務(wù)時集群管理器可以自己提供依賴的 jar。而且 spark-hive 在 maven 官網(wǎng)的生命周期就被給定是 provided。
沒有 spark-hive 依賴的 jar 包大小 9M (之前是 144M),分別在 CDP 和 CDH 上執(zhí)行導(dǎo)入導(dǎo)出操作。結(jié)果:
-
CDP 集群測試通過。
-
CDH 集群異常。猜測是原生 spark3 和 hive-metastore:2.1.1-cdh6.3.3 不兼容(發(fā)行版有時會在原生基礎(chǔ)上做改動),改用方式(3)中的配置后導(dǎo)入導(dǎo)出功能正常。
如果集群采用發(fā)行版部署,大版本下各組件兼容的可能性更大些。而且當(dāng)頻繁調(diào)試 Java jar 功能時 9M 大小縮短了上傳時間,效率也變高了。文章來源:http://www.zghlxwxcb.cn/news/detail-655587.html
小結(jié)
通過配置的方式可以指定 spark 使用的 hiveMetastore。優(yōu)先使用集群自帶的依賴可以在一定程度上減少組件不兼容異常。Java jar 包中只管應(yīng)用程序怎么寫,依賴讓集群提供,可以解除 jar 包與某個大數(shù)據(jù)集群的強(qiáng)綁定關(guān)系。但外部配置只是一種解決方案,如果要與工程結(jié)合還需要根據(jù)場景需求進(jìn)一步設(shè)計實現(xiàn)方案并做實驗。文章來源地址http://www.zghlxwxcb.cn/news/detail-655587.html
到了這里,關(guān)于解決執(zhí)行 spark.sql 時版本不兼容的一種方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!