数据仓库Hive编程:Hive知识点集合
从早期的互联网主流大爆发开始,主要的搜索引擎公司和电子商务公司就一直在和不断增长的数据进行较量。最近,社交网站也遇到了同样的问题。如今,许多组织已经意识到他们所收集的数据是让他们了解他们的用户,提高业务在市场上的表现以及提高基础架构效率的一个宝贵的资源。
Hadoop生态系统就是为处理如此大数据集而产生的一个合乎成本效益的解决方案。Hadoop实现了一个特别的计算模型,也就是MapReduce,其可以将计算任务分割成多个处理单元然后分散到一群家用的或服务器级别的硬件机器上,从而降低成本并提供水平可伸缩性。这个计算模型的下面是一个被称为Hadoop分布式文件系统(HDFS)的分布式文件系统。这个文件系统是“可插拔的”,而且现在已经出现了几个商用的和开源的替代方案。
不过,仍然存在一个挑战,那就是用户如何从一个现有的数据基础架构转移到Hadoop上,而这个基础架构是基于传统关系型数据库和结构化查询语句(SQL)的。对于大量的SQL用户(包括专业数据库设计师和管理员,也包括那些使用SQL从数据仓库中抽取信息的临时用户)来说,这时就需要Hive了。Hive提供了一个被称为Hive查询语言(简称HiveQL或HQL)的SQL方言,来查询存储在Hadoop集群中的数据。
SQL知识分布广泛的一个原因是:它是一个可以有效地、合理地且直观地组织和使用数据的模型。即使对于经验丰富的Java开发工程师来说,将这些常见的数据运算对应到底层的MapReduce Java API也是令人畏缩的。Hive可以帮助用户来做这些苦活,这样用户就可以集中精力关注于查询本身了。Hive可以将大多数的查询转换为MapReduce任务,进而在介绍一个令人熟悉的SQL抽象的同时,拓宽Hadoop的可扩展性。
Hive最适合于数据仓库应用程序,使用该应用程序进行相关的静态数据分析,不需要快速响应给出结果,而且数据本身不会频繁变化。
Hive不是一个完整的数据库。Hadoop以及HDFS的设计本身约束和局限性地限制了Hive所能胜任的工作。其中最大的限制就是Hive不支持记录级别的更新、插入或者删除操作。但是用户可以通过查询生成新表或者将查询结果导入到文件中。同时,因为Hadoop是一个面向批处理的系统,而MapReduce任务的启动过程需要消耗较长的时间,所以Hive查询延时比较严重。传统数据库中在秒级别可以完成的查询,在Hive中,即使数据集相对较小,往往也需要执行更长的时间。最后需要说明的是,Hive不支持事务。因此,Hive不支持OLTP(联机事务处理)所需的关键功能,而更接近成为一个OLAP(联机分析技术)工具。但是我们将会看到,由于Hadoop本身的时间开销很大,并且Hadoop所被设计用来处理的数据规模非常大,因此提交查询和返回结果是可能具有非常大的延时的,所以Hive并没有满足OLAP中的“联机”部分,至少目前并没有满足。
如果用户需要对大规模数据使用OLTP功能的话,那么应该选择使用一个NoSQL数据库,例如,和Hadoop结合使用的HBase及Cassandra。如果用户使用的是Amazon弹性MapReduce计算系统(EMR)或者弹性计算云服务(EC2)的话,也可以使用DynamoDB。用户甚至可以和这些数据库(还包括其他一些数据库)结合来使用Hive。
因此,Hive是最适合数据仓库应用程序的,其可以维护海量数据,而且可以对数据进行挖掘,然后形成意见和报告等。因为大多数的数据仓库应用程序是使用基于SQL的关系型数据库实现的,所以Hive降低了将这些应用程序移植到Hadoop上的障碍。用户如果懂得SQL,那么学习使用Hive将会很容易。如果没有Hive,那么这些用户就需要去重新学习新的语言和新的工具后才能进行生产。
同样地,相对于其他Hadoop语言和工具来说,Hive也使得开发者将基于SQL的应用程序移植到Hadoop变得更加容易。不过,和大多数SQL方言一样,HiveQL并不符合ANSI SQL标准,其和Oracle,MySQL,SQL Server支持的常规SQL方言在很多方面存在差异(不过,HiveQL和MySQL提供的SQL方言最接近)。
因此,本博客商业智能的《数据仓库Hive编程》系列共有两个目的。其一,提供了一个针对所有读者的介绍。这个介绍会比较综合,并且会使用例子来进行讲解。适用的读者包括开发者、数据库管理员和架构师,以及其他(如商业分析师等)非技术类用户。其二,针对开发者和Hadoop管理员等需要深入了解Hive技术细节的用户提供了更详尽的讲述,以帮助这些读者学习如何优化Hive查询性能,如何通过用户自定义函数和自定义数据格式等,来个性化使用Hive。
Hadoop生态综述
我们在《Hadoop与Spark等大数据框架介绍》介绍了Hadoop生态,这里在以介绍Hive为目再次介绍一下Hadoop生态。
MapReduce
虽然用户无需精通MapReduce就可以使用Hive,但是理解MapReduce的基本原理将帮有助于用户了解Hive在底层是如何运作的,以及了解如何才能更高效地使用Hive。
MapReduce是一种计算模型,该模型可将大型数据处理任务分解成很多单个的、可以在服务器集群中并行执行的任务。这些任务的计算结果可以合并在一起来计算最终的结果。
MapReduce编程模型是由谷歌(Google)开发的。Google通过一篇很有影响力的论文对这个计算模型进行了描述,本书附录部分可查看到该论文,名为《MapReduce:大数据之上的简化数据处理》。一年后,另一篇名为《Google文件系统》的论文介绍了Google文件系统。这两篇论文启发了道·卡丁(Doug Cutting)开发了Hadoop。
MapReduce这个术语来自于两个基本的数据转换操作:map过程和reduce过程。一个map操作会将集合中的元素从一种形式转换成另一种形式。在这种情况下,输入的键-值对会被转换成零到多个键-值对输出。其中,输入和输出的键必须完全不同,而输入和输出的值则可能完全不同。
在MapReduce计算框架中,某个键的所有键-值对都会被分发到同一个reduce操作中。确切地说,这个键和这个键所对应的所有值都会被传递给同一个Reducer。reduce过程的目的是将值的集合转换成一个值(例如对一组数值求和或求平均值),或者转换成另一个集合。这个Reducer最终会产生一个键-值对。再次说明一下,输入和输出的键和值可能是不同的。需要说明的是,如果job不需要reduce过程的话,那么也是可以无reduce过程的。
Hadoop提供了一套基础设施来处理大多数困难的工作以保证任务能够执行成功。例如,Hadoop决定如果将提交的job分解成多个独立的map和reduce任务来执行,它就会对这些task进行调度并为其分配合适的资源,决定将某个task分配到集群中哪个位置(如果可能,通常是这个task所要处理的数据所在的位置,这样可以最小化网络开销)。它会监控每一个task以确保其成功完成,并重启一些失败的task。
Hadoop分布式文件系统(也就是HDFS),或者一个同类的分布式文件系统,管理着集群中的数据。每个数据块(block)都会被冗余多份(通常默认会冗余3份),这样可以保证不会因单个硬盘或服务器的损坏导致数据丢失。同时,因为其目标是优化处理非常大的数据集,所以HDFS以及类似的文件系统所使用的数据块都非常大,通常是64MB或是这个值的若干倍。这么大的数据块可以在硬盘上连续进行存储,这样可以保证以最少的磁盘寻址次数来进行写入和读取,从而最大化提高读写性能。
MapReduce计算框架中的输入和输出的基本数据结构是键值对。当Mapper进程启动后,其将会被频繁调用来处理文件中的每行文本。每次调用中,传递给Mapper的键是文档中这行的
起始位置的字符偏移量。对应的值是这行对应的文本。
Hive
当用户真正使用Hadoop的API来实现算法时,甚至有更多的底层细节需要用户自己来控制。这是一个只适用于有经验的Java开发人员的工作,因此也就将Hadoop潜在地放在了一个非程序员用户无法触及的位置,即使这些用户了解他们想使用的算法。
事实上,许多这些底层细节实际上进行的是从一个任务到下一个任务的重复性工作,例如,将Mapper和Reducer一同写入某些数据操作构造这样的底层的繁重的工作,通过过滤得到所需数据的操作,以及执行类似SQL中数据集键的连接 (JOIN)操作等。不过幸运的是,存在一种方式,可以通过使用“高级”工具自动处理这些情况来重用这些通用的处理过程。
这也就是引入Hive的原因。Hive不仅提供了一个熟悉SQL的用户所能熟悉的编程模型,还消除了大量的通用代码,甚至是那些有时是不得不使用Java编写的令人棘手的代码。
这就是为什么Hive对于Hadoop是如此重要的原因,无论用户是DBA还是Java开发工程师。Hive可以让你花费相当少的精力就可以完成大量的工作。
有好几种方式可以与Hive进行交互。在《数据仓库Hive编程》系列中,我们将主要关注于CLI,也就是命令行界面。对于那些更喜欢图形用户界面的用户,可以使用现在逐步出现的商业和开源的解决方案,例如Karmasphere发布的一个商业产品,Cloudera提供的开源的Hue项目,以及Qubole提供的“Hive即服务”方式等。
Hive发行版中附带的模块有CLI,一个称为Hive网页界面(HWI)的简单网页界面,以及可通过JDBC、ODBC和一个Thrift服务器进行编程访问的几个模块。
所有的命令和查询都会进入到Driver(驱动模块),通过该模块对输入进行解析编译,对需求的计算进行优化,然后按照指定的步骤执行(通常是启动多个MapReduce任务来执行)。当需要启动MapReduce任务时,Hive本身是不会生成Java MapReduce算法程序的。相反,Hive通过一个表示“job执行计划”的XML文件驱动执行内置的、原生的Mapper和Reducer模块。换句话说,这些通用的模块函数类似于微型的语言翻译程序,而这个驱动计算的“语言”是以XML形式编码的。
Hive通过和JobTracker通信来初始化MapReduce任务,而不必部署在JobTracker所在的管理节点上执行。在大型集群中,通常会有网关机专门用于部署像Hive这样的工具。在这些网关机上可远程和管理节点上的JobTracker通信来执行任务。通常,要处理的数据文件是存储在HDFS中的,而HDFS是由NameNode进行管理的。
Metastore(元数据存储)是一个独立的关系型数据库(通常是一个MySQL实例),Hive会在其中保存表模式和其他系统元数据。在我们将详细进行讨论。
尽管本书是关于Hive的,不过还是有必要提及其他的一些高级工具,这样用户可以根据需求进行选择。Hive最适合于数据仓库程序,对于数据仓库程序不需要实时响应查询,不需要记录级别的插入、更新和删除。当然,Hive也非常适合于有一定SQL知识的用户。不过,用户的某些工作可能采用其他的工具会更容易进行处理。
Pig
Hive的替代工具中最有名的就是Pig了。Pig是由Yahoo!开发完成的,而同时期Fackbook正在开发Hive。Pig现在同样也是一个和Hadoop紧密联系的顶级Apache项目。
假设用户的输入数据具有一个或者多个源,而用户需要进行一组复杂的转换来生成一个或者多个输出数据集。如果使用Hive,用户可能会使用嵌套查询(正如我们将看到的)来解决这个问题,但是在某些时刻会需要重新保存临时表来控制复杂度。
Pig被描述成一种数据流语言,而不是一种查询语言。在Pig中,用户需要写一系列的声明语句来定义某些关系和其他一些关系之间的联系,这里每个新的关系都会执行新的数据转换过程。Pig会查找这些声明,然后创建一系列有次序的MapReduce任务,来对这些数据进行转换,直到产生符合用户预期的计算方式所得到的最终结果。
这种步进式的数据“流”可以比一组复杂的查询更加直观。也因此,Pig常用于ETL(数据抽取,数据转换和数据装载)过程的一部分,也就是将外部数据装载到Hadoop集群中,然后转换成所期望的数据格式。
Pig的一个缺点就是其所使用的定制语言不是基于SQL的。这是可以理解的,因为Pig本身就不是被设计为一种查询语言的,但是这也意味着不适合将SQL应用程序移植到Pig中,而经验丰富的SQL用户可能需要投入更高的学习成本来学习Pig。然而, Hadoop团队通常会将Hive和Pig结合使用,对于特定的工作选择合适的工具。
HBase
如果用户需要Hive无法提供的数据库特性,如行级别的更新,快速的查询响应时间,以及支持事务的话,就需要考虑HBase了。 HBase是一个分布式的、可伸缩的数据存储,其支持行级别的数据更新、快速查询和行级事务,但其不支持多行事务。
HBase的设计灵感来自于谷歌的BigTable,不过HBase并没有实现BigTable的所有特性。HBase支持的一个重要特性就是列存储,其中的列可以组织成列族。列族在分布式集群中物理上是存储在一起的。这就使得当查询场景涉及的列只是所有列的一个子集时,读写速度会快得多。因为不需要读取所有的行然后丢弃大部分的列,而是只需读取需要的列。
可以像键值存储一样来使用HBase,其每一行都使用了一个唯一键来提供非常快的速度读写这一行的列或者列族。HBase还会对每个列保留多个版本的值(按照时间戳进行标记),版本数量是可以配置的,因此,如果需要,可以“时光倒流”回退到之前的某个版本的值。
HBase使用HDFS(或其他某种分布式文件系统)来持久化存储数据。为了可以提供行级别的数据更新和快速查询,HBase也使用了内存缓存技术对数据和本地文件进行追加数据更新操作日志。持久化文件将定期地使用附加日志更新进行更新等操作。HBase没有提供类似于SQL的查询语言,但是Hive现在已经可以和HBase结合使用了。
Cascading、Crunch及其他
Apache Hadoop生态系统之外还有几个“高级”语言,它们也在Hadoop之上提供了不错的抽象来减少对于特定任务的底层编码工作。为了叙述的完整性,下面我们列举其中的一些来进行介绍。所有这些都是JVM(Java虚拟机)库,可用于像Java、Clojure、Scala、JRuby、Groovy和Jython,而不是像Hive和Pig一样使用自己的语言工具。
使用这些编程语言既有好处也有弊端。它使这些工具很难吸引熟悉SQL的非程序员用户。不过,对于开发工程师来说,这些工具提供了图灵完全的编程语言的完全控制。Hive和Pig都是图灵完全性的。当我们需要Hive本身没有提供的额外功能时,我们需要学习如何用Java编码来扩展Hive功能。