你好,我是黄金。今天这期复习课,我们一起来回顾和总结下,Facebook在2009年所发表的Hive的论文。

Hive介绍

2008年以前,Facebook的数据仓库构建在商用的RDBMS上。随着数据量的增加,一些需要每天执行的批处理作业,单次运行时间已经超过了一天,因此优化数据仓库成为迫在眉睫的任务。后来Facebook把数据搬到了Hadoop上,原来需要花一天多才能跑完的作业,现在花几个小时就能跑完,执行速度快了很多。

不过使用Hadoop并不容易,尤其是对那些不熟悉MapReduce的人来说,即使是写一个简单的查询,也要花上几个小时。工程师们就在想,能不能把分析师熟悉的表、分区之类的SQL概念,引入Hadoop世界,一来可以让分析人员使用自己已经掌握的工具,二来可以把编写脚本的时间,从几个小时缩短成几分钟。

于是,就有了我们今天要介绍的Hive,而它所使用的查询语言就是和SQL非常类似的HiveQL。

数据模型

想要把MapReduce任务变成SQL语句,需要先把数据结构化,才能用SQL语句查询。像传统的RDBMS一样,Hive的数据通过来抽象,数据由多行记录构成,一行记录包含多个字段,每个字段有特定的类型。

Hive天然支持的基础类型包括整数、浮点数和字符串,天然支持的复杂类型包括数组、字典和结构体,这些类型足以让我们应付大多数业务场景。同时,Hive也允许用户自定义字段类型,便于我们应付特殊的业务场景。

和RDBMS不同的地方在于,Hive通常采用宽表结构,把一个对象的所有字段保存在一张表中。这么做的目的是为了避免表与表之间的Join操作,Join操作一般需要跨服务器交互,成本高而且性能差。

Hive的数据表支持分区和分桶操作。

我想这和它们要解决的问题领域相关。Bigtable善于处理随机读写,一次操作仅仅涉及几条记录,动态分区的方式有助于快速定位记录所在的分区,分散访问热点。Hive主要执行批处理作业,一次扫描大量连续的数据,数据只要按照过滤条件静态分区即可。这是因为过滤条件下的所有数据都要读,不会因为横向拆分成更多文件,就可以少读一些数据。

数据存储

Hive的数据表存储在HDFS上,一张表对应了HDFS的一个目录。对数据表分区,就是以分区字段和值为目录名,创建子目录,分区数据保存在子目录中。对数据表分桶,就是把原来的一个文件,拆分成多个文件,有几个桶就拆成几个文件。至于分桶后的文件放在哪里,就看是在表上分桶,还是在分区上分桶。如果是在表上分桶,就存储在表的根目录,如果是在分区上分桶,就存储在对应的分区目录。

Hive通过序列化器Serializer和反序列化器Deserializer,指定应用层数据模型和存储层数据模型之间的转换规则。比如以什么作为行分隔符,又以什么作为字段分隔符。

Hive通过文件格式指定数据如何存储到文件中。比如是以文本格式存储,还是以二进制格式存储,还是使用列式存储。

系统架构

Hive的整个系统架构并不复杂,总共分成三个部分。

如果是一张大表和一张小表Join,应该先找个规则,把大表的数据分区,为每一个分区启动一个任务,把小表的数据拷贝到每个任务中执行Join操作,这种方法被称为广播哈希Join。如果是一张大表和另一张大表Join,应该通过Join字段,把两张表的数据按相同的方式分区,在每个分区上分别执行Join操作,这种方法被称为分区哈希Join

Hive的优化器,会自动识别应该采用哪一种方法执行Join操作。随着Hive的版本升级,已经投入使用的HiveQL还可以利用新版的优化器获得更好的性能,这些好处是直接写MapReduce任务所不具备的。

当然,HiveQL最终还是要翻译成MapReduce任务,每一个Mapper或Reducer任务读取数据后,都需要Metastore中的元数据信息来解析数据。为了降低Metastore被请求的频率,驱动器在生成执行计划时,就已经把所有需要用到的元数据,写到了执行计划中,Mapper或Reducer任务只需要通过执行计划文件,就能得到元数据信息。

通过观察Hive的系统架构,我们可以发现,Hive并没有对MapReduce形成强依赖,所以我们今天可以看到Hive on Spark这样的项目。借助Spark,HiveQL能够执行得更快。这是因为Spark采用了内存管理中间数据,不像MapReduce一样,每个任务都要把结果写到文件系统。而且Spark是数据流引擎,不用等到上一个任务全部结束,才开始执行下一个任务,只要上一个任务有输出一点内容,下一个任务就可以开始执行。

好了,到这里Hive的核心内容我们就复习完了。如果你有其他关于HIve的论文的学习思考,期待在留言区一起探讨。

评论