Hortonworks于2013年提出将Tez作为另一个计算引擎以提高Hive的性能。Spark则是最初由加州大学伯克利分校开发的分布式计算引擎,借助于其灵活的DAG执行模式、对内存的充分利用,以及RDD所能表达的丰富语义,Spark受到了Hadoop社区的广泛关注。在成为Apache顶级项目之后,Spark更是集成了流处理、图计算、机器学习等功能,是业界公认最具潜力的下一代通用计算框架。鉴于此,Hive社区于2014年推出了Hive on Spark项目(HIVE-7292),将Spark作为继MapReduce和Tez之后Hive的第三个计算引擎。该项目由Cloudera、Intel和MapR等几家公司共同开发,并受到了来自Hive和Spark两个社区的共同关注。目前Hive on Spark的功能开发已基本完成,并于2015年1月初合并回trunk,预计会在Hive下一个版本中发布。本文将介绍Hive on Spark的设计架构,包括如何在Spark上执行Hive查询,以及如何借助Spark来提高Hive的性能等。另外本文还将介绍Hive on Spark的进度和计划,以及初步的性能测试数据。
背景
Hive on Spark是由Cloudera发起,由Intel、MapR等公司共同参与的开源项目,其目的是把Spark作为Hive的一个计算引擎,将Hive的查询作为Spark的任务提交到Spark集群上进行计算。通过该项目,可以提高Hive查询的性能,同时为已经部署了Hive或者Spark的用户提供了更加灵活的选择,从而进一步提高Hive和Spark的普及率。
在介绍Hive on Spark的具体设计之前,先简单介绍一下Hive的工作原理,以便于大家理解如何把Spark作为新的计算引擎供给Hive使用。
在Hive中, 一条SQL语句从用户提交到计算并返回结果,大致流程如下图所示(Hive 0.14中引入了基于开销的优化器(Cost Based Optimizer,CBO),优化的流程会略有不同。
Hive on Spark总体的设计思路是,尽可能重用Hive逻辑层面的功能;从生成物理计划开始,提供一整套针对Spark的实现,比如SparkCompiler、SparkTask等,这样Hive的查询就可以作为Spark的任务来执行了。以下是几点主要的设计原则。
尽可能减少对Hive原有代码的修改。这是和之前的Shark设计思路最大的不同。Shark对Hive的改动太大以至于无法被Hive社区接受,Hive on Spark尽可能少改动Hive的代码,从而不影响Hive目前对MapReduce和Tez的支持。同时,Hive on Spark保证对现有的MapReduce和Tez模式在功能和性能方面不会有任何影响。
对于选择Spark的用户,应使其能够自动的获取Hive现有的和未来新增的功能。
尽可能降低维护成本,保持对Spark依赖的松耦合。
基于以上思路和原则,具体的一些设计架构如下。
新的计算引擎
Hive的用户可以通过hive.execution.engine来设置计算引擎,目前该参数可选的值为mr和tez。为了实现Hive on Spark,我们将spark作为该参数的第三个选项。要开启Hive on Spark模式,用户仅需将这个参数设置为spark即可。
这里主要是指使用Hive的操作符对数据进行处理。Spark为RDD提供了一系列的转换(Transformation),其中有些转换也是面向SQL的,如groupByKey、join等。但如果使用这些转换(就如Shark所做的那样),就意味着我们要重新实现一些Hive已有的功能;而且当Hive增加新的功能时,我们需要相应地修改Hive on Spark模式。有鉴于此,我们选择将Hive的操作符包装为Function,然后应用到RDD上。这样,我们只需要依赖较少的几种RDD的转换,而主要的计算逻辑仍由Hive提供。
由于使用了Hive的原语,因此我们需要显式地调用一些Transformation来实现Shuffle的功能。下表中列举了Hive on Spark使用的所有转换。
SparkContext是用户与Spark集群进行交互的接口,Hive on Spark应该为每个用户的会话创建一个SparkContext。但是Spark目前的使用方式假设SparkContext的生命周期是Spark应用级别的,而且目前在同一个JVM中不能创建多个SparkContext(请参考SPARK-2243)。这明显无法满足HiveServer2的应用场景,因为多个客户端需要通过同一个HiveServer2来提供服务。鉴于此,我们需要在单独的JVM中启动SparkContext,并通过RPC与远程的SparkContext进行通信。
除了一般的单元测试以外,Hive还提供了Qfile Test,即运行一些事先定义的查询,并根据结果判断测试是否通过。Hive on Spark的Qfile Test应该尽可能接近真实的Spark部署环境。目前我们采用的是local-cluster的方式(该部署模式主要是Spark进行测试时使用,并不打算让一般用户使用),最终的目标是能够搭建一个Spark on YARN的Mini Cluster来进行测试。
理论上来说,Hive on Spark对于Spark集群的部署方式没有特别的要求,除了local以外,RemoteDriver可以连接到任意的Spark集群来执行任务。在我们的测试中,Hive on Spark在Standalone和Spark on YARN的集群上都能正常工作(需要动态添加Jar包的查询在yarn-cluster模式下还不能运行,请参考HIVE-9425)。
随着项目开发接近尾声,我们也已经开始对Hive on Spark进行初步的性能测试。测试集群由10台亚马逊AWS虚拟机组成,Hadoop集群由HDP 2.2搭建,测试数据为320GB的TPC-DS数据集。目前的测试用例有6条,包含了自定义的查询以及TPC-DS中的两条查询。由于Hive主要用于处理ETL查询,因此我们在TPC-DS中选取用例时,选取的是较为接近ETL查询的用例(TPC-DS中的用例主要针对交互型查询,Impala、SparkSQL等引擎更适合此类查询)。为了更有针对性,测试主要是对Hive on Spark和Hive on Tez进行性能对比,最新的一组测试数据如下图所示(鉴于项目仍在开发中,该数据仅供参考)。
图5:Hive on Spark vs. Hive on Tez
图中横坐标为各个测试用例,纵坐标为所用时间,以秒为单位。
总结
Hive on Spark由多家公司协作开发,从项目开始以来,受到了社区的广泛关注,HIVE-7292更是有超过140位用户订阅,已经成为Hive社区中关注度最高的项目之一。由于涉及到两个开源项目,Hive社区和Spark社区的开发人员也进行了紧密的合作。在开发过程中发现的Spark的不足之处以及新的需求得到了积极的响应(请参考SPARK-3145)。通过将Spark作为Hive的引擎,现有的用户拥有了更加灵活的选择。Hive与Spark两个社区均将从中受益。
本文阐述了Hive on Spark的总体设计思想,并详细介绍了几个重要的实现细节。最后总结了项目进展情况,以及最新的性能数据。希望通过本文能为用户更好的理解和使用Hive on Spark带来帮助。