spark操作hive(可解決絕大部分的問題)、sparksql操作hive

目標:

想要完成使用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.Predef..conforms()Lscala/Predef$lesslesscolon$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]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章