谈谈hive on Spark和Spark with hive

993 阅读5分钟

Spark SQL 其中一类非常典型的场景是与 Hive 做集成、构建分布式数据仓库。数据仓库指的是一类带有主题、聚合层次较高的数据集合,它的承载形式,往往是一系列 Schema 经过精心设计的数据表。在数据分析这类场景中,数据仓库的应用非常普遍。

在 Hive 与 Spark 这对“万金油”组合中,Hive 擅长元数据管理,而 Spark 的专长是高效的分布式计算,二者的结合可谓是“强强联合”。

spark与hive的集成方式有两种,一类是从 Spark 的视角出发,叫做Spark with Hive;一类,是从 Hive 的视角出发,叫做Hive on Spark。

Spark with Hive

Hive Metastore利用 RDBMS 来存储数据表的元信息,如表名、表类型、表数据的 Schema、表(分区)数据的存储路径、以及存储格式,等等。形象点说,Metastore 就像是“户口簿”,它记录着分布式文件系统中每一份数据集的“底细”。

Spark SQL 通过访问 Hive Metastore,即可扩充数据访问来源。而这,就是 Spark with Hive 集成方式的核心思想。在这种集成模式下,Spark 是主体,Hive Metastore 不过是 Spark 用来扩充数据来源的辅助工具。

作为开发者,我们可以通过 3 种途径来实现 Spark with Hive 的集成方式,它们分别是:

  1. 创建 SparkSession,访问本地或远程的 Hive Metastore;
  2. 通过 Spark 内置的 spark-sql CLI,访问本地 Hive Metastore;
  3. 通过 Beeline 客户端,访问 Spark Thrift Server。

SparkSession + Hive Metastore

先从SparkSession 访问 Hive Metastore 说起。首先,启动 Hive Metastore。

代码

hive --service metastore

Hive Metastore 启动之后,我们需要让 Spark 知道 Metastore 的访问地址,也就是告诉他数据源的“户口簿”藏在什么地方。

要传递这个消息,有两种办法。

  • 一种是在创建 SparkSession 的时候,通过 config 函数来明确指定 hive.metastore.uris 参数。
  • 另一种方法是让 Spark 读取 Hive 的配置文件 hive-site.xml,该文件记录着与 Hive 相关的各种配置项,其中就包括 hive.metastore.uris 这一项。把 hive-site.xml 拷贝到 Spark 安装目录下的 conf 子目录,Spark 即可自行读取其中的配置内容。

举个例子,假设 Hive 中有一张名为“salaries”的薪资表,每条数据都包含 id 和 salary 两个字段,表数据存储在 HDFS,获取数据的代码为

import org.apache.spark.sql.SparkSession
import  org.apache.spark.sql.DataFrame
 
val hiveHost: String = _
// 创建SparkSession实例
val spark = SparkSession.builder()
                   .config("hive.metastore.uris", s"thrift://hiveHost:9083")
                   .enableHiveSupport()
                   .getOrCreate()
 
// 读取Hive表,创建DataFrame
val df: DataFrame = spark.sql(“select * from salaries”)
 
df.show
 
/** 结果打印
+---+------+
| id|salary|
+---+------+
|  1| 26000|
|  2| 30000|
|  4| 25000|
|  3| 20000|
+---+------+
*/

对于数据文件,我们利用利用 createTempView函数来创建临时表,临时表创建好之后,就可以使用 SparkSession 的 sql API 来提交 SQL 查询语句。那么有了Hive Metastore,我们可以直接使用sql API 去访问 Hive 中现有的表。

更重要的是,createTempView 函数创建的临时表,它的生命周期仅限于 Spark 作业内部,这意味着一旦作业执行完毕,临时表也就不复存在,没有办法被其他应用复用。Hive 表则不同,它们的元信息已经持久化到 Hive Metastore 中,不同的作业、应用、甚至是计算引擎,如 Spark、Presto、Impala,等等,都可以通过 Hive Metastore 来访问 Hive 表。

总结下来,在 SparkSession + Hive Metastore 这种集成方式中,Spark 对于 Hive 的访问,仅仅涉及到 Metastore 这一环节,对于 Hive 架构中的其他组件,Spark 并未触及。换句话说,在这种集成方式中,Spark 仅仅是“白嫖”了 Hive 的 Metastore,拿到数据集的元信息之后,Spark SQL 自行加载数据、自行处理,如下图所示。

image.png 在第一种集成方式下,通过 sql API,你可以直接提交复杂的 SQL 语句,也可以在创建 DataFrame 之后,再使用各种算子去实现业务逻辑。

Beeline + Spark Thrift Server

其过程是使用 Beeline 客户端,去连接 Spark Thrift Server,从而完成 Hive 表的访问与处理。

通过 Hive Server 2 接入的查询请求,经由 Hive Driver 的解析、规划与优化,交给 Hive 搭载的计算引擎付诸执行。相应地,查询结果再由 Hiver Server 2 返还给 Beeline 客户端,如下图右侧的虚线框所示。

Beeline 原本是 Hive 客户端,通过 JDBC 接入 Hive Server 2。Hive Server 2 可以同时服务多个客户端,从而提供多租户的 Hive 查询服务。由于 Hive Server 2 的实现采用了 Thrift RPC 协议框架,因此很多时候我们又把 Hive Server 2 称为“Hive Thrift Server 2”。

image.png

Spark Thrift Server 脱胎于 Hive Server 2,在接收查询、多租户服务、权限管理等方面,这两个服务端的实现逻辑几乎一模一样。它们最大的不同,在于 SQL 查询接入之后的解析、规划、优化与执行。

Hive Server 2 的“后台”是 Hive 的那套基础架构。而 SQL 查询在接入到 Spark Thrift Server 之后,它首先会交由 Spark SQL 优化引擎进行一系列的优化。

Hive on Spark

Hive 的松耦合设计,使得它的 Metastore、底层文件系统、以及执行引擎都是可插拔、可替换的。

在执行引擎方面,Hive 默认搭载的是 Hadoop MapReduce,但它同时也支持 Tez 和 Spark。所谓的“Hive on Spark”,实际上指的就是 Hive 采用 Spark 作为其后端的分布式执行引擎,如下图所示。

image.png

其实hive on MapReduce或者Hive on Spark或者Hive on Tez其实就是执行引擎的切换。不论 Hive 选择哪一种执行引擎,引擎仅仅负责任务的分布式计算,SQL 语句的解析、规划与优化,通通由 Hive 的 Driver 来完成。

为了搭载不同的执行引擎,Hive 还需要做一些简单的适配,从而把优化过的执行计划“翻译”成底层计算引擎的语义。 例如:在 Hive on Spark 的集成方式中,Hive 在将 SQL 语句转换为执行计划之后,还需要把执行计划“翻译”成 RDD 语义下的 DAG,然后再把 DAG 交付给 **Spark Core ** 付诸执行。

在 Hive on Spark 这种集成模式下,Hive 与 Spark 衔接的部分是 Spark Core,而不是 Spark SQL。