马上注册,结交更多数据大咖,获取更多知识干货,轻松玩转大数据
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
作者:李程,北京邮电大学,友盟数据平台工程师,北京市种子管理站站长。上篇讲到Hive构建数据仓库中的模式设计,深入浅出地介绍了模式设计中的三个关键要素。本篇我们从底层存储数据结构出发,讲一讲Hive是如何组织数据的。
行式存储 v.s. 列式存储
传统数据库大多基于行(Row-based)实现数据存储,即一行行的记录。此类存储结构对大多数的传统数据工作都是非常有效的,下面我们先来回顾一下数据库系统中数据工作的概念:
在Row-based数据库中,一行记录中的每一列都是紧挨着另一列存放在硬盘中的,行之间也成线性存储。这样的模式十分适用于OLTP工作,由于每次操作对象都是某几行记录,每次查询只需要从硬盘中加载最少的数据。
OLAP更倾向于访问百万、千万甚至上亿条记录。传统的行式存储(Row-oriented Storage)使得我们需要花费时间加载每一行,而真正需要的数据可能仅是每行中的几个数据列而已。如果存储结构基于列(Column-based),那么单列查询就只需要加载硬盘中的最小列块,这种方式在磁盘IO上是比较高效的。正是如此,我们可以说OLAP促成了列式存储(Columnar Storage)的出现。
下图展示了Row-oriented Storage和Columnar Storage的原理:
当然,列式存储也并非万能。单纯给Column-based数据表加索引,并不能使其在OLTP工作流上表现高效。就删改记录等需求而言,查询任务需要加载磁盘中的很多列块才能整合一条完整的记录。如果一个数据表的列项过于丰富,那么Columnar Storage反而会加重OLTP工作流的磁盘I/O负载。相比而言,Row-oriented Storage则更适合对单一整行记录的处理。
如何选择存储结构取决于你的企业对OLTP/OLAP业务的需求。目前还有一些行列混合存储技术结合了两种架构的优势。例如针对Columnar Storage提出的列组(Column Group)概念,多个列形成一个组。如果访问的列属于同一组,查询工作流就可以避免多个数据列的合并。这种结构能够同时满足OLTP和OLAP的查询需求。
Columnar Storage从一开始就是面向大数据环境下数据仓库的数据分析而产生的。下文我们就从Hive的实际应用中介绍Columnar Storage的优点。
Hive的数据格式
目前Hive所支持的数据格式如下:
我们根据Hive文档的描述,简单介绍几类Columnar Storage的数据格式。
RCFileRCFile(Record Columnar File)是为基于MapReduce的数据仓库系统设计的一个列式存储结构。Hive在0.6.0版本后纳入了RCFile。
RCFile采用二进制的key/value对来存储数据。首先,它在行上进行水平分块,然后每块又以列式的方式垂直切割。RCFile将一个数据块的metadata作为一条记录的key,而数据块本身作为value。这样结合行式和列式的优点,满足了高效的数据加载和查询处理,以及有效利用存储空间等需求。下图为RCFile的数据分块原理:
ORCORC(Optimized Row Columnar)在RCFile基础上改进,提供了更加高效的数据存取格式。和RCFile相比,ORC有如下优势:
单个Hive Task输出单个文件,减小文件系统负载。 支持datetime、decimal和其他复杂类型(struct、list、map和union)。 文件内含轻量级索引。减少不必要的扫描,高效定位记录。 基于数据类型的块模式压缩。例如String和Integer可以采用不同的压缩方式。 同一文件可以利用多个RecordReader并发读取。 支持免扫描进行文件分块。 读写文件时,绑定I/O所需的最大内存空间。 文件的metadata采取Protocol Buffers格式,允许灵活的属性增删。
ParquetApache基金会的Parquet是在hadoop生态圈中受到广泛支持的列式存储格式。Parquet借鉴Dremel文章中提到的Shredding and assembly算法,将复杂、嵌套的数据结构展开来存储。同时它还支持非常高效的压缩方法和编码格式。目前很多实际应用也证实了这种压缩和编码的优越性能。下面是Parquet目前所支持的项目和数据描述语言:
Hive 0.13后,Parquet已经被作为原生态支持而正式加入Apache Hive项目。在之前的版本中,你需要将parquet-hive-bundle.jar作为第三方支持包加载到Hive中方可使用Parquet。
由于篇幅原因,这里就不详尽介绍各类数据格式的具体实现机制,有兴趣的读者可以参阅本文的扩展阅读。
Why Columnar Storage?
前文《(一)模式设计》中提到过,友盟的离线日志数据采用的是PB(Google Protocol Buffers) + LZO(Lempel–Ziv–Oberhumer)。即便能被ProtobufSerDe解析,PB.LZO文件格式的数据仍然不适用于OLAP。所以友盟数据仓库混合使用Parquet/Text等不同类型的存储结构来适配业务需求。
下面从实战角度出发,用一系列的实验给读者展示在数据仓库中使用Columnar Storage的优势。
我们选择以下维度作为PB.LZO(LZO压缩)、RCFile、ORC以及Parquet的性能标准:
数据压缩比 任务执行时间 Map输入量 平均CPU时间开销
为了达成这些指标的测试,我们选取910GB的文本数据。这些数据一方面转换为PB.LZO格式保存;另一方面采用上述后三种Columnar Storage数据格式保存,并以Snappy或Gzip/Zlib压缩。实验结果如下:
(a)数据压缩比率对比图
从图(a)中可以得知,Columnar Storage比Row-oriented Storage具有更高的压缩比。同一列内的数据比之不同列之间,具有更高的相似度。所以列块比行的压缩效果更加明显。
(b)任务执行时间对比图
图(b)表示任务执行时间。由于任务执行时间受诸多因素(例如集群计算资源闲忙情况、实验次数是否能充分消除随机性、网络吞吐等等)影响,我们这里只将其作为参考。
复杂查询会增加Reduce的计算时间,而Columnar Storage技术并不会加速Reduce的业务逻辑计算。所以我们选择的测试任务均为: select count(col1) from table。
(c)Map输入量对比图
图(c)展示的文件输入量对比充分显示了Columnar Storage的优势。相比PB.LZO,采用各类Columnar Storage技术的任务Map输入量都仅占各自数据存储大小的一半以下,是PB.LZO输入量的约三分之一。Parquet和ORC在这里表现最优。值得一提的是,就执行select count(*) from table而言,Parquet和ORC可以将Map输入量缩减到100MB以下,这几乎不造成太大的网络I/O开销。
Columnar Storage如何降低文件输入量,取决于其列组的分割方式。越细粒度的列组越能降低简单OLAP工作流的文件读取量。但是多列交叉查询就会导致频繁的数据列合并,从而降低查询效率。所以我们需要平衡列式存储查询效率和文件吞吐量之间的收益。
(d)平均CPU时间开销对比图
图(d)中,CPU开销从小到大依次是:ORC-Snappy > ORC-Zlib > RCFile-Snappy > Paquet-Snappy > Parquet-Gzip >RCFile-Gzip > PB.LZO。实验中我们通过设置不同的min.split.size调整Mapper数均为600,最大程度降低环境因素影响。
上述实验中,以ORC-Snappy为例,性能优化比之PB.LZO如下:
存储空间额外压缩30%; 查询效率提高50%左右; 文件输入减少约66%; CPU开销降低70%以上。
我们可以看到,各类Columnar Storage技术在OLAP工作流上的优势是很明显的。
本文也在这里结束了。希望能帮助大家了解到Hive的相关知识,也敬请大家期待下文:(三)Hive架构。
扩展阅读http://db.csail.mit.edu/projects/cstore/abadi-sigmod08.pdf https://cwiki.apache.org/confluence/display/Hive/RCFile https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORC https://cwiki.apache.org/confluence/display/Hive/Parquet
|