SparkSQL 整合 Hive
Hive 是一个外部的数据存储和查询引擎, 所以如果 Spark 要访问 Hive 的话, 就需要先整合 Hive。
整合什么 ?
- MetaStore, 元数据存储
SparkSQL 内置的有一个 MetaStore, 通过嵌入式数据库 Derby 保存元信息, 但是对于生产环境来说, 还是应该使用 Hive 的 MetaStore, 一是更成熟, 功能更强, 二是可以使用 Hive 的元信息。 - 查询引擎
SparkSQL 内置了 HiveSQL 的支持, 所以无需整合。
为什么要开启 Hive 的 MetaStore?
Hive 的 MetaStore 是一个 Hive 的组件, 一个 Hive 提供的程序, 用以保存和访问表的元数据, 整个 Hive 的结构大致如下:
由上图可知道, 其实 Hive 中主要的组件就三个, HiveServer2 负责接受外部系统的查询请求, 例如 JDBC, HiveServer2 接收到查询请求后, 交给 Driver 处理, Driver 会首先去询问 MetaStore 表在哪存, 后 Driver 程序通过 MR 程序来访问 HDFS 从而获取结果返回给查询请求者。
而 Hive 的 MetaStore 对 SparkSQL 的意义非常重大, 如果 SparkSQL 可以直接访问 Hive 的 MetaStore, 则理论上可以做到和 Hive 一样的事情, 例如通过 Hive 表查询数据。
而 Hive 的 MetaStore 的运行模式有三种:
- 内嵌 Derby 数据库模式
这种模式不必说了, 自然是在测试的时候使用, 生产环境不太可能使用嵌入式数据库, 一是不稳定, 二是这个 Derby 是单连接的, 不支持并发。 - Local 模式
Local 和 Remote 都是访问 MySQL 数据库作为存储元数据的地方, 但是 Local 模式的 MetaStore 没有独立进程, 依附于 HiveServer2 的进程。 - Remote 模式
和 Local 模式一样, 访问 MySQL 数据库存放元数据, 但是 Remote 的 MetaStore 运行在独立的进程中。
我们显然要选择 Remote 模式, 因为要让其独立运行, 这样才能让 SparkSQL 一直可以访问.
一、Hive 开启 MetaStore
Step 1: 修改 hive-site.xml
<!--默认创建表存放的位置-->
<property>
<name>hive.metastore.warehouse.dir</name>
<value>/user/hive/warehouse</value>
</property>
<!--指定mysql中hive数据库的访问路径,如果不存在,会创建-->
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://node01:3306/hive?createDatabaseIfNotExist=true</value>
</property>
<!--指定连接数据库的驱动-->
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
</property>
<!--指定连接数据库的用户名 root-->
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>username</value>
</property>
<!--指定连接数据库的密码 root-->
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>password</value>
</property>
<!-- 如果要运行在一个服务上,可以配置,告诉他,metastore不是本地运行的 -->
<property>
<name>hive.metastore.local</name>
<value>false</value>
</property>
<!-- 指定 hive metastore 服务请求的 uri 地址 -->
<property>
<name>hive.metastore.uris</name>
<value>thrift://node01:9083</value> //当前服务器
</property>
以上配置文件中的参数如果没有的添加上即可
Step 2: 启动 Hive MetaStore(nohup命令后台启动)
nohup /export/servers/hive/bin/hive --service metastore 2>&1 >> /var/log.log &
二、SparkSQL 整合 Hive 的 MetaStore
即使不去整合 MetaStore, Spark 也有一个内置的 MateStore, 使用 Derby 嵌入式数据库保存数据, 但是这种方式不适合生产环境, 因为这种模式同一时间只能有一个 SparkSession 使用, 所以生产环境更推荐使用 Hive 的 MetaStore。
SparkSQL 整合 Hive 的 MetaStore 主要思路就是要通过配置能够访问它, 并且能够使用 HDFS 保存 WareHouse, 这些配置信息一般存在于 Hadoop 和 HDFS 的配置文件中, 所以可以直接拷贝 Hadoop 和 Hive 的配置文件到 Spark 的配置目录
cd /export/servers/hadoop/etc/hadoop
cp hive-site.xml core-site.xml hdfs-site.xml /export/servers/spark/conf/
scp -r /export/servers/spark/conf node02:/export/servers/spark/conf
scp -r /export/servers/spark/conf node03:/export/servers/spark/conf
Spark 需要 hive-site.xml 的原因是, 要读取 Hive 的配置信息, 主要是元数据仓库的位置等信息
Spark 需要 core-site.xml 的原因是, 要读取安全有关的配置
Spark 需要 hdfs-site.xml 的原因是, 有可能需要在 HDFS 中放置表文件, 所以需要 HDFS 的配置
注意事项:如果不希望通过拷贝文件的方式整合 Hive, 也可以在 SparkSession 启动的时候, 通过指定 Hive 的 MetaStore 的位置来访问, 但是更推荐整合的方式。
特点:只有使用SparkShell或者SparkSubmit的时候才能享受这个整合的便利
三、访问 Hive 表
3.1 准备-----在 Hive 中创建表
第一步, 需要先将文件上传到集群中, 使用如下命令上传到 HDFS 中:
hdfs dfs -mkdir -p /dataset
hdfs dfs -put studenttabl10k /dataset/
第二步, 使用 Hive 或者 Impala 执行如下 SQL
CREATE DATABASE IF NOT EXISTS spark01;
USE spark01;
CREATE EXTERNAL TABLE student
(
name STRING,
age INT,
gpa string
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
LINES TERMINATED BY '\n'
STORED AS TEXTFILE
LOCATION '/dataset/hive';
LOAD DATA INPATH '/dataset/studenttab10k' OVERWRITE INTO TABLE student;
studenttab10k 文件数据可以自行模拟,字段是student表字段。
3.2 通过 SparkSQL 查询 Hive 的表
通过 SparkSQL 可以直接创建 Hive 表, 并且使用 LOAD DATA 加载数据,通过spark-shell命令进入命令行界面 Spark session available as ‘spark’.
val createTableStr =
"""
|CREATE EXTERNAL TABLE student
|(
| name STRING,
| age INT,
| gpa string
|)
|ROW FORMAT DELIMITED
| FIELDS TERMINATED BY '\t'
| LINES TERMINATED BY '\n'
|STORED AS TEXTFILE
|LOCATION '/dataset/hive'
""".stripMargin
spark.sql("CREATE DATABASE IF NOT EXISTS spark02")
spark.sql("USE spark02")
spark.sql(createTableStr)
spark.sql("LOAD DATA INPATH '/dataset/studenttab10k' OVERWRITE INTO TABLE student")
spark.sql("select * from student limit 100").show()
目前 SparkSQL 支持的文件格式有 sequencefile, rcfile, orc, parquet, textfile, avro, 并且也可以指定 serde 的名称。
3.3 使用 SparkSQL 处理数据并保存进 Hive 表
前面都在使用 SparkShell 的方式来访问 Hive, 编写 SQL, 通过 Spark 独立应用的形式也可以做到同样的事, 但是需要一些前置的步骤, 如下
Step 1: 导入 Maven 依赖
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-hive_2.11</artifactId>
<version>${spark.version}</version>
<dependency>
Step 2: 配置 SparkSession
如果希望使用 SparkSQL 访问 Hive 的话, 需要做两件事
- 开启 SparkSession 的 Hive 支持(必须),经过这一步配置, SparkSQL 才会把 SQL 语句当作 HiveSQL 来进行解析
- 设置 WareHouse 的位置(必须),虽然 hive-stie.xml 中已经配置了 WareHouse 的位置, 但是在 Spark 2.0.0 后已经废弃了 hive-site.xml 中设置的 hive.metastore.warehouse.dir, 需要在 SparkSession 中设置 WareHouse 的位置
- 设置 MetaStore 的位置
val spark = SparkSession
.builder()
.appName("hive example")
// 设置 WareHouse 的位置
.config("spark.sql.warehouse.dir", "hdfs://node01:8020/dataset/hive")
// 设置 MetaStore 的位置
.config("hive.metastore.uris", "thrift://node01:9083")
// 开启 Hive 支持
.enableHiveSupport()
.getOrCreate()
配置好了以后, 就可以通过 DataFrame 处理数据, 后将数据结果推入 Hive 表中了, 在将结果保存到 Hive 表的时候, 可以指定保存模式 SaveMode(SaveMode是指定保存模式,是枚举类:Append、Overwrite、ErrorIfExists、Ignore)
val schema = StructType(
List(
StructField("name", StringType),
StructField("age", IntegerType),
StructField("gpa", FloatType)
)
)
val studentDF = spark.read
.option("delimiter", "\t")
.schema(schema)
.csv("dataset/studenttab10k")
val resultDF = studentDF.where("age < 50")
// 通过 mode 指定保存模式, 通过 saveAsTable 保存数据到 Hive
resultDF.write.mode(SaveMode.Overwrite).saveAsTable("spark_integrition1.student")
3.4 使用IDEA来举例
object HiveAccess {
def main(args: Array[String]): Unit = {
// 1.创建SparkSession
// 1.2 指定Metastore的位置
// 1.3 指定Warehouse的位置
val spark = SparkSession.builder()
.appName("hive_example")
// 1.1 开启Hive支持
.enableHiveSupport()
// thrift是RPC框架,协议
.config("hive.metastore.uris","thrift://node01:9083")
.config("spark.sql.warehouse.dir","hdfs://node01:8020/dataset/hive")
.getOrCreate()
import spark.implicits._
// 2.读取数据
// 2.1 上传HDFS,因为要在集群中执行,没办法保证程序在哪个机器中执行,所以,要把文件上传到所有的机器中,才能读取本地文件
// 上传到HDFS就可以解决这个问题,所有的机器都可以读取HDFS中的文件,它是一个外部系统
// 2.2 使用DF读取数据
val schema: StructType = StructType(
List(
StructField("name", StringType),
StructField("age", IntegerType),
StructField("gpa", FloatType)
)
)
val df: DataFrame = spark.read
.option("delimiter", "\t")
.schema(schema)
.csv("hdfs://node01:8020/dataset/studenttab10k")
val resultDF: Dataset[sql.Row] = df.where('age > 50)
// 3.写入数据,使用写入表的API,saveAsTable
resultDF.write
.mode(SaveMode.Overwrite)
.saveAsTable("spark02.student")
}
}