为了优化MapReduce及MR之前的各种工具的性能,在Hadoop内建的数据存储格式外,又涌现了一批各种各样的存储方式。如优化Hive性能的RCFile,以及配合Impala实现出Google Dremel功能(类似甚至是功能的超集)的Parquet等。今天就来一起学习一下HDFS中数据存储的进化历程。
数据摆放结构
数据摆放结构(data placement structure),顾名思义,就是数据如何在HDFS中放置和存储的。这种摆放结构对于像Hive这种,HDFS之上的查询工具来说是非常重要的,摆放的结构和策略会直接影响Hive查询引擎的实现和性能。从Hive的角度来看,数据摆放结构就是:怎样从Hive中关系表的逻辑视图映射到HDFS块数据的物理存储。
从更高的层次来看,不仅仅是HDFS这种分布式系统上的应用,data placement对于传统数据库、NoSQL等系统也都是很重要的:
通常来说,有以下三种数据摆放结构:
? 水平的行存储结构
? 垂直的列存储结构
? 混合型的存储结构
下面就依次看一下这三种存储方式的优缺点。
水平的行存储结构
行存储是最传统的存储方式,经典模型是NSM(The N-ary Storage Model),其优缺点也很明显。优点就是数据加载非常快,因为一行数据都是放在一起的。同时对各种动态workload有很强的适应能力(具体指?)。而缺点就是:
? 因为无法避免读取不必要的列,而无法提供海量数据的高性能查询。
? 此外,由于混合存储了不同类型的数据列,实现高压缩也很不容易。
下图是NSM的典型实现,主要由page header,body(数据),以及trailer(每行数据在当前page中位置的偏移量)。下图中还指出了一个缺点,就是由于每次都加载了不必要的列,导致缓存中塞满了无用数据。
下面来看两个NSM的例子。首先是MySQL的Innodb引擎,InnoDB的tablespace模仿了Oracle中的概念,而page结构(page layout, page structure或row format)类似NSM模型。我们主要关注其中的page结构,其他row-based的数据库也都是类似的存储结构。
再来看一下Hadoop的例子。我们最常使用的两种格式就是TextFile和SequenceFile,它们都是按行存储。TextFile读起来很方便,但比较占空间,Gzip压缩后不支持块分离。Hadoop默认内部使用SequenceFile二进制存储,支持可分离的压缩算法。同时,由于当我们把文件写入HDFS时会分成block块,读取时是整块读取后按分隔符解析,逐行传送给map任务(HDFS就是为流式读取大文件而设计),不存在RDBMS中的随机读一行,所以block尾部不需要trailer部分来保存偏移量。
垂直的列存储结构
列存储弥补了行存储的两个劣势,可以只读取目标列的数据,并提供高压缩比。但是缺点是:
? 由于数据存储的分散,查询时会产生高昂的数据重建(tuple reconstruction)开销。
? 并且更新、删除等对数据的修改操作也会比较麻烦。
典型的实现是DSM模型,在1985年就已经提出来了。
下图就是HDFS中列式存储的样子。
现今有很多列式存储的NoSQL数据库,在Hadoop中最典型的例子就是HBase了。(待补充:HFile结构,以及HBase如何解决列式存储的性能开销等问题)
混合型的存储结构
PAX(The Partition Attributes Across Model)是一种典型的混合型实现,与前面两种传统存储方式的具体模型比较:
引用一篇文章:
NSM(N-ary Storage Model),即基于行的存储模型。随着硬件的发展,NSM对于缓存的利用率较低。NSM在每个磁盘页面中连续的存储记录,使相对页面的偏移记录每条记录的开始。
DSM(Decomposition Storage Model)。列存储模型并不是一个新鲜的概念,在1985年就已经提出,2005年左右随着数据分析应用的广泛开展获得新生。对数据的使用,特别是分析的需求,常常只使用一条记录的一部分数据。为了减少IO的消耗,提出了“分解存储模型”。DSM将关系垂直分为n个子关系,属性仅当需要时才加以存取访问。对于涉及多个属性的查询来说需要额外的开销用于连接子关系。
PAX(Partition Attribute Across)。PAX是记录在页面中的混合布局方式,结合了NSM和DSM的优点,避免了对主存不需要的访问。PAX首先将尽可能多的关系记录采用NSM方式加以存储。在每个页面内,使用按属性和minipage进行类似于DSM的存储。在顺序扫描时,PAX充分利用了缓存的资源。同时,所有的记录都位于相同的页面。对于记录的重构操作,仅仅在minipage之间进行,并不涉及跨页的操作。相对于DSM来说,对于多属性的查询来说PAX优于DSM,因为DSM需要更多的跨页重构时间。
混合存储模型,我们可以将所有的数据都理解为由Key/Value/Description(column name)构成的三元组存储模型。KV模型允许你按照你想要的模式来组织数据的存储,如果应用总是按照行来访问的(比如总是访问某个用户的大部分数据),那么就可以把数据按照同一个Key组织在一起(实际上就是NSM),而如果某个应用总是分析汇总查询,可以按照Description(column name)将数据组织在一起(DSM或者PAX的实现)。
Record Columnar File(RCFile)借鉴了PAX存储模型,混合了行式和列式存储。通过先进行水平分区,再垂直分区,并且保证同一行的数据一定在同一个结点。
RCFile基于HDFS,一个表可以包含多个块,每块内按行组(row group)进行组织。每个行组包含:用来分隔行组的sync标记,元数据头,以及按列式存储的表数据。其中元数据头和表数据是分别独立压缩的。元数据头使用RLE(runtime length encoding)算法,而表数据使用gzip算法,并配合延迟解压技术(lazy decompression)。RCFile只支持追加(append)写数据。
Parquet
Parquet是Cloudera和Twitter合作的项目,实现了Dremel论文中定义的数据模型,能够以列式存储的二维表来表示嵌套记录,同时也支持像Pig和Hive等行式查询引擎。Parquet的存储结构与RCFile有雷同之处,例如RowGroup包含多个column,而每个column则由page组成,page中的每一项是由repetition level、definition level和value组成的三元组。
Parquet中使用多种编码压缩技术。首先,对于不重复值较少的列可以进行字典编码(dictionary encoding),例如不重复值<5w个,这要比gzip, lzo, snappy等重型算法要更好、更快。此外,对于字典编码后的列值,repetition和definition level这种小整数,还可以进行位压缩(bit packing),用能够装下这些小整数的最少的位来保存它们。最后,结合着前两种方法,还可以进一步进行RLE(run length encoding)压缩,这对definition level这种比较稀疏的列来说效果比较好。
参考资料
1 RCFile: A Fast and Space-efficient Data Placement Structure
2 A Multi-resolution Block Storage Model for Database Design
3 Data Page Layouts for Relational Databases on Deep Memory Hierarchies
4 InnoDB Internals: InnoDB File Formats and Source Code Structure
5 Parquet: An Open Columnar Storage for Hadoop