1.3 列存储
数据可以面向行或面向列的格式来存储。在面向行格式中,一行的所有列或字段存储在一起。这里的一行,可以是CSV文件中的一行,或者是数据库表中的一条记录。当数据以面向行格式保存时,第一行后面是第二行,接着是第三行,以此类推。面向行存储对于主要执行数据的CRUD(创建、读取、更新、删除)操作的应用来说是完美的。这些应用一次操作数据中的一行。
然而,面向行存储对于分析类应用来说不够高效。这样的应用要对数据集的列进行操作。更重要的是,这些应用只读取和分析跨越多行的列的一个小子集。因此,读取所有列是对内存、CPU周期和硬盘I/O的浪费,这是一个昂贵的操作。
面向行存储的另一个缺点是数据无法高效地压缩。一条记录可能由多种不同数据类型的列构成,一行的熵就会很高。压缩算法不适用于压缩多样化数据。因此,使用面向行格式存储在硬盘上的一个表格比用列存储格式所生成的文件更大。更大的文件不仅要耗费更多的硬盘空间,还会影响应用的性能,因为硬盘I/O与文件大小成正比,而硬盘I/O是一个昂贵的操作。
面向列存储系统以列的形式在硬盘上存储数据。列中的所有单元保存在一起,或者连续地保存。比如,当以列格式在硬盘上保存一个表格时,所有行的第一列首先保存,然后是所有行的第二列,接着是第三列,以此类推。列存储在分析类应用方面比面向行存储更加高效,使分析更加迅速,而所需硬盘空间更小。
下一节讨论Hadoop生态系统中3种常用的列存储文件格式。
1.3.1 RCFile
RCFile(列式记录文件)是一种构建于HDFS之上用来存储Hive表格的列存储格式。它实现了一种混合的列存储格式。RCFile首先把表格分割成行组(row group),然后以列格式保存每一个行组。所有行组分布在整个集群上。
RCFile使得我们可以同时利用列存储和Hadoop MapReduce的优势。因为行组分布在整个集群上,所以它们可以并行处理。一个节点上,行的列存储有助于高效的压缩和更快的分析。
1.3.2 ORC
ORC(Optimized Row Columnar)是另一种高效存储结构化数据的列存储文件格式。相对RCFile,它有很多优势。比如,它保存行索引,使得查询中可以快速搜索一个指定行。因为它基于数据类型采用块模式的压缩,所以它能提供更好的压缩效果。另外,可以用zlib或Snappy在基于数据类型的列级别的压缩之上进行通用压缩。
和RCFile类似,ORC文件格式把表格分割成可配置大小的条带(见图1-4)。默认的条带大小为250MB。一个条带类似于RCFile中的一个行组,但是每个条带不仅包含行数据,还包括索引数据和条带脚部。条带脚部含有流位置的目录。索引数据包括每一列的最小值和最大值以及行索引。ORC文件格式在一个条带中为每10000行保存一个索引。在每个条带内部,ORC文件格式使用特定数据类型的编码技术来压缩列,如:针对整型列的行程编码和针对字符串列的字典编码。还可以使用zlib或Snappy之类的通用压缩编解码器来进一步压缩列。
所有条带之后是文件脚部,其中包含文件中条带的列表、条带中的行数和各个列的数据类型,还包括每一列的统计数据,比如:数目、最小值、最大值和总数。文件脚部之后是附录(postscript)部分,其中包含压缩参数和压缩的脚部大小。
ORC文件格式不仅高效存储数据,还有助于高效查询。应用在一次查询中可以只请求所需的列。同样地,应用可以使用谓词下推来跳跃读取整个行集。
1.3.3 Parquet
Parquet是为Hadoop生态系统而设计的另一个列存储格式。它可以被任何数据处理框架所使用,包括Hadoop MapReduce和Spark。它用来支持复杂的嵌套数据结构。另外,它不仅支持多种数据编码和压缩技术,还可以按列来指定压缩方案。
Parquet实现了一个三层的层次结构来在文件中存储数据(见图1-5)。首先,和RCFile和ORC类似,它在水平方向把表格分割为行组。行组分布在整个集群上,因此可以用任何集群计算框架来并行处理。其次,在每个行组内部,它把列分割为列块。Parquet用术语“列块”来表示行组中一列的数据。一个列块在硬盘上连续存储。层次结构中的第三级是页面。Parquet把列块分割为多个页面。一个页面是编码和压缩的最小单元。一个列块可以包含多个不同类型的交错页面。因此,一个Parquet文件由行组构成,行组中包含列块,而列块中包含一个或多个页面。