parquet和orc
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了parquet和orc相关的知识,希望对你有一定的参考价值。
参考技术A Parquet文件是自解析的,文件中包括该文件的数据和元数据。在HDFS文件系统和Parquet文件中存在如下几个概念:1)HDFS块(Block):它是HDFS上的最小的副本单位,HDFS会把一个Block存储在本地的一个文件并且维护分散在不同的机器上的多个副本,通常情况下一个Block的大小为256M、512M等。
2)HDFS文件(File):一个HDFS的文件,包括数据和元数据,数据分散存储在多个Block中。
3)行组(Row Group):按照行将数据物理上划分为多个单元,每一个行组包含一定的行数,在一个HDFS文件中至少存储一个行组,Parquet读写的时候会将整个行组缓存在内存中,所以如果每一个行组的大小是由内存大的小决定的。
4)列块(Column Chunk):在一个行组中每一列保存在一个列块中,行组中的所有列连续的存储在这个行组文件中。不同的列块可能使用不同的算法进行压缩。
5)页(Page):每一个列块划分为多个页,一个页是最小的编码的单位,在同一个列块的不同页可能使用不同的编码方式。
Parquet文件的格式如下图所示:
可以看出,存储格式中元数据索引信息是被存储在最后的,所以当读取某一行的数据的时候,就需要去定位最后的索引信息,最后才能去读取对应的行数据。元数据包括 Parquet 原始类型定义、Page类型、编码类型、压缩类型等等。
Parquet 支持嵌套结构的数据模型,而非扁平式的数据模型,这是 Parquet 相对其他列存比如 ORC 的一大特点或优势。支持嵌套式结构,意味着 Parquet 能够很好的将诸如 Protobuf,thrift,json 等对象模型进行列式存储。
Parquet 的数据模型也是 schema 表达方式,用关键字 message 表示。每个字段包含三个属性,repetition属性(required/repeated/optional)、数据类型(primitive基本类型/group复杂类型)及字段名。如:
和Parquet类似,ORC文件也是以二进制方式存储的,所以是不可以直接读取,ORC文件也是自解析的,它包含许多的元数据,这些元数据都是同构ProtoBuffer进行序列化的。ORC的文件结构如下图,其中涉及到如下的概念:
ORC文件:保存在文件系统上的普通二进制文件,一个ORC文件中可以包含多个stripe,每一个stripe包含多条记录,这些记录按照列进行独立存储,对应到Parquet中的row
group的概念。
文件级元数据:包括文件的描述信息PostScript、文件meta信息(包括整个文件的统计信息)、所有stripe的信息和文件schema信息。
stripe:一组行形成一个stripe,每次读取文件是以行组为单位的,一般为HDFS的块大小,保存了每一列的索引和数据。
stripe元数据:保存stripe的位置、每一个列的在该stripe的统计信息以及所有的stream类型和位置。
row group:索引的最小单位,一个stripe中包含多个row group,默认为10000个值组成。
stream:一个stream表示文件中一段有效的数据,包括索引和数据两类。索引stream保存每一个row group的位置和统计信息,数据stream包括多种类型的数据,具体需要哪几种是由该列类型和编码方式决定。
在ORC文件中保存了三个层级的统计信息,分别为文件级别、stripe级别和row group级别的,他们都可以用来根据Search ARGuments(谓词下推条件)判断是否可以跳过某些数据,在统计信息中都包含成员数和是否有null值,并且对于不同类型的数据设置一些特定的统计信息。
读取ORC文件是从尾部开始的,第一次读取16KB的大小,尽可能的将Postscript和Footer数据都读入内存。文件的最后一个字节保存着PostScript的长度,它的长度不会超过256字节,PostScript中保存着整个文件的元数据信息,它包括文件的压缩格式、文件内部每一个压缩块的最大长度(每次分配内存的大小)、Footer长度,以及一些版本信息。在Postscript和Footer之间存储着整个文件的统计信息(上图中未画出),这部分的统计信息包括每一个stripe中每一列的信息,主要统计成员数、最大值、最小值、是否有空值等。
接下来读取文件的Footer信息,它包含了每一个stripe的长度和偏移量,该文件的schema信息(将schema树按照schema中的编号保存在数组中)、整个文件的统计信息以及每一个row group的行数。
处理stripe时首先从Footer中获取每一个stripe的其实位置和长度、每一个stripe的Footer数据(元数据,记录了index和data的的长度),整个striper被分为index和data两部分,stripe内部是按照row group进行分块的(每一个row group中多少条记录在文件的Footer中存储),row group内部按列存储。每一个row group由多个stream保存数据和索引信息。每一个stream的数据会根据该列的类型使用特定的压缩算法保存。在ORC中存在如下几种stream类型:
PRESENT:每一个成员值在这个stream中保持一位(bit)用于标示该值是否为NULL,通过它可以只记录部位NULL的值
DATA:该列的中属于当前stripe的成员值。
LENGTH:每一个成员的长度,这个是针对string类型的列才有的。
DICTIONARY_DATA:对string类型数据编码之后字典的内容。
SECONDARY:存储Decimal、timestamp类型的小数或者纳秒数等。
ROW_INDEX:保存stripe中每一个row group的统计信息和每一个row group起始位置信息。
在初始化阶段获取全部的元数据之后,可以通过includes数组指定需要读取的列编号,它是一个boolean数组,如果不指定则读取全部的列,还可以通过传递SearchArgument参数指定过滤条件,根据元数据首先读取每一个stripe中的index信息,然后根据index中统计信息以及SearchArgument参数确定需要读取的row group编号,再根据includes数据决定需要从这些row group中读取的列,通过这两层的过滤需要读取的数据只是整个stripe多个小段的区间,然后ORC会尽可能合并多个离散的区间尽可能的减少I/O次数。然后再根据index中保存的下一个row group的位置信息调至该stripe中第一个需要读取的row group中。
ORC文件格式只支持读取指定字段,还不支持只读取特殊字段类型中的指定部分。
使用ORC文件格式时,用户可以使用HDFS的每一个block存储ORC文件的一个stripe。对于一个ORC文件来说,stripe的大小一般需要设置得比HDFS的block小,如果不这样的话,一个stripe就会分别在HDFS的多个block上,当读取这种数据时就会发生远程读数据的行为。如果设置stripe的只保存在一个block上的话,如果当前block上的剩余空间不足以存储下一个strpie,ORC的writer接下来会将数据打散保存在block剩余的空间上,直到这个block存满为止。这样,下一个stripe又会从下一个block开始存储。
由于ORC中使用了更加精确的索引信息,使得在读取数据时可以指定从任意一行开始读取,更细粒度的统计信息使得读取ORC文件跳过整个row group,ORC默认会对任何一块数据和索引信息使用ZLIB压缩,因此ORC文件占用的存储空间也更小,这点在后面的测试对比中也有所印证。
文件格式对比ORC-Parquet,存储格式对比Gzip-Bzip2-Snappy
Parquet和ORC对比
1.存储文件的压缩比总结:ORC > Parquet
2.存储文件的查询速度总结:查询速度相近,ORC好一点点
3.可兼容的平台:ORC常用于Hive、Presto;
Parquet常用于Impala、Drill、Spark、Arrow;
Avro常用于Kafka、Druid
impala和spark现在很流行所以parquet存储格式流行
4.orc支持事务表分桶update操作,parquet完全不支持
5.处理深层次文件 parquet设计之初就是为了处理嵌套式数据如json。除此之外没有比ORC好太多的地方
Gzip-Bzip2-Snappy压缩格式对比
名 | GZip | BZip2 | LZO | Snappy |
HadoopCodec类 | GzipCodec | Bzip2Codec | LzopCodec | SnappyCodec |
算法 | Deflate | GZip2 | LZO | Snappy |
文件扩展名 | .gz | .bz2 | .lzo | .snappy |
Hadoop内嵌 | 是 | 是 | 否 | 否 |
可切片 | 否 | 是 | 是 | 否 |
压缩比(测试值) | 2 (13.4%) | 1 (13.2%) | 3 (20.5%) | 4 (22.2%) |
压缩速率 | 3 (21MB/S) | 4 (2.4MB/S) | 2 (135MB/S) | 1 (172MB/S) |
解压速率 | 3 (118MB/s) | 4 (9.5MB/s) | 1 (410MB/s) | 2 (409MB/S) |
特点 | GZip压缩比高,大部分Linux系统自带Gzip命令,Hadoop原生就支持使用很方便;速度较慢,而且不支持切片 | BZip2压缩比最高,但速度实在太慢了 | 压缩比尚可,速度快,支持切片(需要建立索引,且文件修改后要重建索引,还需将 InputFormat 指定为Lzo )。支持hadoop native库,但不是Hadoop自带,需要自己安装。 | 压缩比最低,但速度最快,但不支持切片。支持hadoop native库,但不是Hadoop自带,需要自己安装。需要注意的是,实际生产环境中Snappy的表现通常比 LZO 好,应当进行测试对比再决定。 |
使用场景 | 如果压缩后大小和Block差不多大可以使用。也适合磁盘不富裕要求压缩比且对压缩时间无特别要求的场景,如异步离线压缩归档,比如HBase写入后的刷盘 | 适合很老的、极低频使用的历史文件(冷数据)归档 Bzip2压缩解压的速度都非常慢,现在使用愈来愈少 | 特点是支持切片 | 实时写入的Hive底层HDFS文件可用LZO方式压缩 Snappy解压速度最快,对解压速度要求高的场景使用比较多,Hadoop平台上使用最广泛 |
以上是关于parquet和orc的主要内容,如果未能解决你的问题,请参考以下文章
在 HIVE 中使用 CDH 5.4 和 Spark 1.3.0 和 Parquet 表的 PySpark 中的 Parquet 错误