spark-submit報錯:沒有合適驅動
來自專欄 Spark
我的原創地址:
spark-submit報錯:Exception in thread "main" java.sql.SQLException:No suitable driver前言
最近寫了一個用spark連接oracle,然後將mysql所有的表保存到hive中的程序,在本地eclipse里運行沒有問題,想在集群上跑一下,看看在集群上性能如何,但是用spark-submit 提交程序時拋出一個異常Exception in thread "main" java.sql.SQLException: No suitable driver,一開始以為spark-submit提交時找不到oracle 驅動jar,折騰了半天才發現是代碼問題。
1、猜測是否是缺失oracle驅動
由於在本地沒有問題,所以不會想到是代碼問題,根據提示想到的是spark-submit找不到oracle驅動,因為maven或sbt倉庫里沒有oracle驅動,在本地跑的時候,是將oracle驅動下載到本地,然後在eclipse設置build path就可以了。
但是我在spark-submit 里已經通過--jars 載入oracle驅動了:
spark-submit --class com.dkl.leanring.spark.sql.Oracle2HiveDemo --jars ojdbc5-11.2.0.3.jar spark-scala_2.11-1.0.jar
開始以為自己用法不對,但是上網搜了一下,發現就是這麼用的~ 然後嘗試用--driver-class-path、--driver-library-path等都沒成功。
2、sbt-assembly打包
網上查的sbt-assembly打包可以將所有依賴的jar包包括你寫的代碼全部打包在一起,於是嘗試了一下 首先在項目目錄中project/plugins.sbt添加
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")resolvers += Resolver.url("bintray-sbt-plugins", url("http://dl.bintray.com/sbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns)
其中0.14.5為版本號,需要與自己sbt對應上,否則報錯,可在http://dl.bintray.com/sbt/sbt-plugin-releases/com.eed3si9n/sbt-assembly/查看版本 然後在項目對應目錄下執行sbt,然後輸入plugins,即可看到sbtassembly插件了,如下:
$ sbt[info] Loading settings from plugins.sbt ...[info] Loading global plugins from C:Users14123.sbt1.0plugins[info] Loading settings from assembly.sbt,plugins.sbt ...[info] Loading project definition from D:workspacespark-scalaproject[info] Loading settings from spark-scala.sbt ...[info] Set current project to spark-scala (in build file:/D:/workspace/spark-scala/)[info] sbt server started at local:sbt-server-8b6c904d40b181717b3fsbt:spark-scala> pluginsIn file:/D:/workspace/spark-scala/ sbt.plugins.IvyPlugin: enabled in spark-scala sbt.plugins.JvmPlugin: enabled in spark-scala sbt.plugins.CorePlugin: enabled in spark-scala sbt.plugins.JUnitXmlReportPlugin: enabled in spark-scala sbt.plugins.Giter8TemplatePlugin: enabled in spark-scala com.typesafe.sbteclipse.plugin.EclipsePlugin: enabled in spark-scala sbtassembly.AssemblyPlugin: enabled in spark-scalasbt:spark-scala>
但是這樣執行sbt-assembly打包會報錯,需要解決jar包衝突(deduplicate)問題 在項目的bulid.sbt里添加如下即可(只是其中一種解決策略,可根據自己項目實際情況自己設定)
assemblyMergeStrategy in assembly := { case m if m.toLowerCase.endsWith("manifest.mf") => MergeStrategy.discard case m if m.startsWith("META-INF") => MergeStrategy.discard case PathList("javax", "servlet", xs @ _*) => MergeStrategy.first case PathList("org", "apache", xs @ _*) => MergeStrategy.first case PathList("org", "jboss", xs @ _*) => MergeStrategy.first case "about.html" => MergeStrategy.rename case "reference.conf" => MergeStrategy.concat case _ => MergeStrategy.first}
其中不同舊版本和新版本sbt寫法不一樣,具體可上網看一下別人的博客或者在官網查看。
這樣就可以sbt-assembly進行打包了,發現這樣打的jar包確實很大,用sbt package打的jar包大小1.48MB,sbt-assembly打的jar包大小194MB,將spark-scala-assembly-1.0.jar上傳到伺服器,然後執行submit,發現還是報同樣的錯,查看一下sbt-assembly日誌,發現確實將oracle驅動載入上了~
3、真正原因
這樣就猜想不是缺少oracle驅動,於是上網查了好多,偶然發現可能是代碼問題,下面是我寫的從oracle取數的部分代碼
val allTablesDF = spark.read .format("jdbc") .option("url", "jdbc:oracle:thin:@192.168.44.128:1521:orcl") .option("dbtable", "(select table_name,owner from all_tables where owner in(BIGDATA))a") .option("user", "bigdata") .option("password", "bigdata") .load()
寫法和我之前寫的spark連接mysql的博客里的寫法是一樣的:Spark Sql 連接mysql 這樣寫在eclipse運行是沒問題的,但是在spark-submit提交時是不行的,需要加上驅動信息
.option("driver", "oracle.jdbc.driver.OracleDriver")
重新打包,再運行,發現果然沒問題
4、總結
4.1
其實在用spark提交之前寫的spark連接mysql的程序也會報統一的錯(如果$SPARK_HOME/jars沒有mysql驅動),和oracle驅動不在sbt倉庫里沒關係。 但是之前在spark-shell里測試spark連接hive時已經將mysql驅動拷貝過去了,所以mysql沒有報錯
4.2
在代碼里加上driver之後再提交如果沒有oracle驅動會報不同的錯
Exception in thread "main" java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver
4.3
通過--jars指定jar和sbt assembly打包都可以,看自己習慣,但通過--jars需要注意先後順序,如果是多個jar以逗號隔開即可 正確:
spark-submit --class com.dkl.leanring.spark.sql.Oracle2HiveDemo --jars ojdbc5-11.2.0.3.jar spark-scala_2.11-1.0.jar spark-submit --jars ojdbc5-11.2.0.3.jar --class com.dkl.leanring.spark.sql.Oracle2HiveDemo spark-scala_2.11-1.0.jar
錯誤:
spark-submit --class com.dkl.leanring.spark.sql.Oracle2HiveDemo spark-scala_2.11-1.0.jar --jars ojdbc5-11.2.0.3.jarspark-submit --jars ojdbc5-11.2.0.3.jar spark-scala_2.11-1.0.jar --class com.dkl.leanring.spark.sql.Oracle2HiveDemo ...
也就是通過sbt package和sbt assembly生成的項目jar包一定要放在最後面
4.4
通過--driver-class-path也可以實現載入額外的jar
spark-submit --class com.dkl.leanring.spark.sql.Oracle2HiveDemo --driver-class-path lib/* spark-scala_2.11-1.0.jar
但是這樣lib下只能一個jar,兩個jar就會報錯,不知道什麼原因~
4.5
將oracle驅動拷貝到$SPARK_HOME/jars,就可以不在代碼里指定driver選項了,而且也不用通過--jars添加oracle驅動,一勞永逸.
cp ojdbc5-11.2.0.3.jar $SPARK_HOME/jarsspark-submit --class com.dkl.leanring.spark.sql.Oracle2HiveDemo spark-scala_2.11-1.0.jar
具體這樣設置可根據實際情況和偏好習慣使用。
附完整代碼(測試用)
比較簡單就不加註釋~
package com.dkl.leanring.spark.sqlimport org.apache.spark.sql.SparkSessionobject Oracle2HiveDemo { def main(args: Array[String]): Unit = { val spark = SparkSession .builder() .appName("Oracle2HiveDemo") .master("local") .enableHiveSupport() .getOrCreate() val allTablesDF = spark.read .format("jdbc") .option("url", "jdbc:oracle:thin:@192.168.44.128:1521:orcl") .option("dbtable", "(select table_name,owner from all_tables where owner in(BIGDATA))a") .option("user", "bigdata") .option("password", "bigdata") .option("driver", "oracle.jdbc.driver.OracleDriver") .load() import spark.implicits._ import spark.sql sql("use oracle_test") allTablesDF.rdd.collect().foreach(row => { val tableName: String = row(0).toString() val dataBase: String = row(1).toString() println(dataBase + "." + tableName) val df = spark.read .format("jdbc") .option("url", "jdbc:oracle:thin:@192.168.44.128:1521:orcl") .option("dbtable", dataBase + "." + tableName) .option("user", "bigdata") .option("password", "bigdata") .option("driver", "oracle.jdbc.driver.OracleDriver") .load() df.write.mode("overwrite").saveAsTable(tableName) }) spark.stop }}
推薦閱讀:
※SparkR專欄[5]—機器學習
※Spark實戰(4)_Master原理剖析與源碼分析
※第七章:完全分散式部署Hadoop
※第四章:克隆虛擬機
TAG:Spark |