目標:
想要完成使用spark在windows的idea下操作hive(對hive中的表查詢等)
最終代碼(java):
import org.apache.spark.sql.SparkSession;
import java.io.Serializable;
/**
* Created by Administrator on 2017/4/3.
*/
public class SQLHiveJava {
public static void main(String[] args) {
SparkSession spark = SparkSession
.builder()
.appName("Java Spark Hive Example")
.master("local[*]")
.config("spark.sql.warehouse.dir","hdfs://mycluster/user/hive/warehouse")
.enableHiveSupport()
.getOrCreate();
spark.sql("show databases").show();
spark.sql("select count(*) from mobike.logs").show();
spark.sql("select * from mobike.logs").show();
}
}
結果展示:
整個過程用了我1天多時間獨立研究,出現各種各樣的問題,而且網上的貼子找了很多,改了又改,最終在自己的堅持下達到結果。
官方文檔:
剛開始我是照着視頻做,但是視頻並沒有做出來,目前大部分視頻都沒有這部分。
於是最終選擇了官方文檔。
先來看下官方文檔的內容:
import java.io.File ;
import java.io.Serializable ;
import java.util.ArrayList ;
import java.util.List ;
import org.apache.spark.api.java.function.MapFunction ;
import org.apache.spark.sql.Dataset ;
import org.apache.spark.sql.Encoders ;
import org.apache.spark.sql.Row ;
import org.apache.spark.sql.SparkSession ;
public static class Record 實現 Serializable {
private int key ;
私有 字符串 值;
public int getKey () {
return key ;
}
public void setKey (int key ) {
this 。key = key ;
}
public String getValue () {
return value ;
}
public void setValue (String value ) {
this 。value = value ;
}
}
// warehouseLocation指向託管數據庫和表的默認位置
String warehouseLocation = new File (“spark-warehouse” )。getAbsolutePath ();
SparkSession spark = SparkSession
。builder ()
。appName (“Java Spark Hive示例” )
。config (“spark.sql.warehouse.dir” , warehouseLocation )
。enableHiveSupport ()
。getOrCreate ();
火花。sql (“CREATE TABLE IF NOT EXISTS src(key INT,value STRING)USING hive” );
火花。sql (“LOAD DATA LOCAL INPATH”examples / src / main / resources / kv1.txt'INTO TABLE src“ );
//查詢以HiveQL
spark 表示。sql (“SELECT * FROM src” )。show ();
// + --- + ------- +
// | key | 值|
// + --- + ------- +
// | 238 | val_238 |
// | 86 | val_86 |
// | 311 | val_311 |
// ...
//也支持聚合查詢。
火花。sql (“SELECT COUNT(*)FROM src” )。show ();
// + -------- +
// | count(1)|
// + -------- +
// | 500 |
// + -------- +
// SQL查詢的結果本身就是DataFrames並支持所有正常的功能。
數據集< Row > sqlDF = spark 。sql (“SELECT鍵,值FROM src WHERE鍵<10 ORDER BY鍵” );
// DataFrames中的項目爲Row類型,允許您按順序訪問每列。
數據集< String > stringsDS = sqlDF 。地圖(
(MapFunction < 行, 字符串>) 行 - > “鍵:” + 行。得到(0 ) + “值:” + 行。得到(1 ),
編碼器。STRING ());
stringsDS 。show ();
// + -------------------- +
// | 值|
// + -------------------- +
// |鍵:0,值:val_0 |
// |鍵:0,值:val_0 |
// |鍵:0,值:val_0 |
// ...
//您還可以使用DataFrames在SparkSession中創建臨時視圖。
List < Record > records = new ArrayList <>();
for (int key = 1 ; key < 100 ; key ++) {
Record record = new Record ();
記錄。setKey (key );
記錄。setValue (“val_” + key );
記錄。添加(記錄);
}
數據集< 行> recordsDF = 火花。createDataFrame (記錄, 記錄。類);
recordsDF 。createOrReplaceTempView (“records” );
//然後,查詢可以將DataFrames數據與存儲在Hive中的數據連接起來。
火花。sql (“SELECT * FROM records r JOIN src s on r.key = s.key” )。show ();
// + --- + ------ + --- + ------ +
// | key | 值|關鍵| 值|
// + --- + ------ + --- + ------ +
// | 2 | val_2 | 2 | val_2 |
// | 2 | val_2 | 2 | val_2 |
// | 4 | val_4 | 4 | val_4 |
// ...
其實官方文檔已經寫的很清楚的,如果經驗足夠,完全不用像我這樣搞了一整天。
但是我當時只看了代碼,文字根本沒看,就算是我當時看了,估計也未必看得懂。
分析官方文檔:
參照官方文檔後開始寫代碼。
第一步要做的是導入依賴。
這個如果實在不清楚,可以根據網上的帖子來粘。
但是也正是如此,一代依賴出現問題,就會產生第一個頭疼的問題。
先來看下我的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>groupId</groupId>
<artifactId>lazy-spark</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<spark.version>2.2.1</spark.version>
<scala.version>2.11</scala.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-sql_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-hive-thriftserver -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive-thriftserver_2.11</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hive/hive-jdbc -->
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>2.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.17</version>
</dependency>
</dependencies>
</project>
先來看一下第一個絆倒大部分人的錯誤,也是讓我很難受的錯誤:
Exception in thread “main” java.lang.NoSuchMethodError: scala.Predefconforms()Lscala/Predef$colon$less;
Exception in thread "main" java.lang.NoSuchMethodError: scala.Predef$.$conforms()Lscala/Predef$$less$colon$less;
at org.apache.spark.util.Utils$.getSystemProperties(Utils.scala:1722)
at org.apache.spark.SparkConf.loadFromSystemProperties(SparkConf.scala:73)
at org.apache.spark.SparkConf.<init>(SparkConf.scala:68)
at org.apache.spark.SparkConf.<init>(SparkConf.scala:55)
at mysql$.main(mysql.scala:9)
at mysql.main(mysql.scala)
......
Unable to instantiate SparkSession with Hive support because Hive classes are not found.
Exception in thread "main" java.lang.IllegalArgumentException: Unable to instantiate SparkSession with Hive support because Hive classes are not found.
at org.apache.spark.sql.SparkSession$Builder.enableHiveSupport(SparkSession.scala:845)
at com.lcc.spark.structed.streaming.KafkaDemo1.main(KafkaDemo1.java:33)
這個問題的主要原因就是一個:
那就是spark和scala的版本不統一。
舉個例子:
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive-thriftserver_2.11</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
注意:2.11是scala版本。2.2.1就是spark版本
不是說這兩個版本必須和我一致。但是你要做的是,其他的依賴都要使用這個版本。否則就會報錯。如果不放心,可以直接粘貼我上面的。
參考鏈接:
https://blog.csdn.net/huoliangwu/article/details/86316054
- 還有一種說法,就是另一個錯誤導致的這個錯誤,那就是,如果你的代碼平時運行的時候會出現hadoop的錯誤,類似這樣等
ERROR Shell: Failed to locate the winutils binary in the hadoop binary path
java.io.IOException: Could not locate executable null\bin\winutils.exe in the Hadoop binaries.
at org.apache.hadoop.util.Shell.getQualifiedBinPath(Shell.java:356)
at org.apache.hadoop.util.Shell.getWinUtilsPath(Shell.java:371)
at org.apache.hadoop.util.Shell.<clinit>(Shell.java:364)
at org.apache.hadoop.util.StringUtils.<clinit>(StringUtils.java:80)
at org.apache.hadoop.security.SecurityUtil.getAuthenticationMethod(SecurityUtil.java:611)
at org.apache.hadoop.security.UserGroupInformation.initialize(UserGroupInformation.java:272)
at org.apache.hadoop.security.UserGroupInformation.ensureInitialized(UserGroupInformation.java:260)
at
這個問題,在我找帖子的時候有人提到過,原文是一定要把這個解決,否則也會報錯,解決方法是配置windows的hadoop環境變量。
解決的帖子貼在下面:
https://blog.csdn.net/csdn_fzs/article/details/7898558
https://www.e-learn.cn/content/qita/707774
拷貝集羣上的配置文件:
第二個問題就很容易解決,你要把hadoop集羣上的以下配置文件拷貝到idea中
warehouseLocation是什麼、spark-warehouse是什麼、spark-warehouse應該些什麼
這個問題其實也困擾了我特別就,直到結果出來以後才確認。
先來看我寫的
hdfs://mycluster/user/hive/warehouse
其中mycluster是我高可用集羣的名字,這個要寫對,要和core-site.xml裏寫的一致,不在多說了。
那麼後面的部分其實也很好理解。如果你的hive是默認配置的話,和我寫的一樣就好。我們都是到存在hive中的數據,最終在hdfs目錄上,那麼後面的其實就是hive數據在你hdfs的目錄
查不到hive的元數據。
如果以上錯誤都解決了
我建議你在代碼中使用如下代碼進行測試。
spark.sql("show databases").show();
爲什麼呢,因爲這個一定會有個結果,而不會產生我們接下來的問題
Table or view not found: mobike
.logs
; line 1 pos 21;
'Aggregate [unresolvedalias(count(1), None)]
± 'UnresolvedRelation mobike
.logs
Exception in thread "main" org.apache.spark.sql.AnalysisException: Table or view not found: `mob`.`lo`; line 1 pos 21;
'Aggregate [unresolvedalias(count(1), None)]
+- 'UnresolvedRelation `mob`.`lo`
org.apache.spark.sql.AnalysisException: Table or view not found: traintext
.train
; line 1 pos 14;
org.apache.spark.sql.AnalysisException: Table or view not found: traintext
.train
; line 1 pos 14;
'Project [*]
± 'UnresolvedRelation traintext
.train
這個錯誤就有意思了,這是我入坑的問題,也是結果出來後才明白的。
我看的視頻到這裏就不講了,他選擇另一種方式來處理hive,但不符合我的要求。
網上很多貼子對這個問題都沒有講清楚,當然還是有很多負責人的帖子最終讓我找到了答案。
首先要聲明的是,如果你按照我的依賴來寫是不會有這個問題的,但是會報其他錯。
回到剛纔,我建議大家用這行代碼調試
spark.sql("show databases").show();
因爲這行代碼一定會給你個結果,可惜結果並不是我們想的樣子。
我們看錯誤結果:
大概就這樣子。
也就是說,我們只能查到一個默認庫,查不到hive中其他自己建的庫。
spark只能查到hive的default庫、spark查詢不到hive中建的表和庫
知識點來了,看清楚!!!
這些官方文檔都寫過了,我們再來分析一下:
!!!
如果沒有部署好的hive,spark確實是會使用內置的hive,但是spark會將所有的元信息都放到spark_home/bin 目錄下,也就是爲什麼配置了spark.sql.warehouse.dir 卻不起作用的原因。而且,就算部署了hive,也需要讓spark識別hive,否則spark,還是會使用spark默認的hive
!!!
驚不驚喜。spark還有自己的內置hive,如果沒有正確的連接到自己的hive,那麼使用的是spark自己的內置庫。所以官方文檔的代碼纔會那樣寫。
那麼怎麼讓spark連接到自己hive呢。
spark連接自己的hive
首先,要連接hive一定要有這=些依賴,缺一不可
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_${scala.version}</artifactId>
<version>${spark.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-hive-thriftserver -->
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive-thriftserver_2.11</artifactId>
<version>2.2.1</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hive/hive-jdbc -->
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-jdbc</artifactId>
<version>2.1.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.17</version>
</dependency>
thriftserver:就是用來連接連接hive元數據的服務
mysql con……:因爲我的hive元數據存在mysql所以要有這個驅動。
如果這些都做好了,那麼會有接下來的問題。
用hive --service metastore來啓動hive
簡單一個hive然後進入shell是不行的。要通過元數據方式啓動。
最後一個問題來了
Exception in thread “main” java.lang.RuntimeException: java.lang.RuntimeException: Unable to instantiate org.apache.hadoop.hive.ql.metadata.SessionHiveMetaStoreClient
所有都做好以後恭喜你最後一個問題,還是元數據的問題,這裏就將一個,也是最容易被忽略的。
那就是,在我們的hive-site.xml中其實少了些東西。
<configuration>
<property>
<name>hive.metastore.uris</name>
<value>thrift://hive服務端ip:9083</value>
</property>
</configuration>
hive服務端指的是你hive的所在節點的ip地址或主機名。
到此爲止所有的都解決完了,再來看下結果:
格式不怎麼好看,希望能幫助大家,如果你還有問題可以聯繫我
郵箱:[email protected]