网站产品页面,购物网站后台流程图,用户服务管理系统,网站后台发布文章文章目录 数据模型Parquet 的原子类型Parquet 的逻辑类型嵌套编码 Parquet文件格式 本文主要参考文献#xff1a;Tom White. Hadoop权威指南. 第4版. 清华大学出版社, 2017.pages 363. Aapche Parquet是一种能有效存储嵌套数据的列式存储格式#xff0c;在Spark中应用较多。 … 文章目录 数据模型Parquet 的原子类型Parquet 的逻辑类型嵌套编码 Parquet文件格式 本文主要参考文献Tom White. Hadoop权威指南. 第4版. 清华大学出版社, 2017.pages 363. Aapche Parquet是一种能有效存储嵌套数据的列式存储格式在Spark中应用较多。
列式存储格式在文件大小和查询性能上表现优秀在列式存储格式下同一列的数据连续保存。一般来说这种做法可以允许更高效的编码方式从而使列式存储格式的文件常常比行式存储格式的同等文件占用更少的空间。
例如对于存储时间戳的列采用的编码方式可以是存储第一个时间戳的值尔后的值则只需要存储与前一个值之间的差根据时间局部性原理即同一时间前后的记录彼此相邻这种编码方式更倾向于占用较小的空间。
查询引擎在执行时能够跳过对本次查询无用的行提高查询性能。在Hadoop生态中还有其他的列式存储如Hive项目中著名的ORCFileOptimized Record Columnar File。
Parquet的突出贡献在于能够以真正的列式存储格式来保存具有深度嵌套结构的数据。在显示世界中具有多级嵌套模式的系统比较普通所以这种能力非常重要。Parquet脱胎于Google发表的一篇关于Dremel的论文它通过一种新颖的技术以扁平的列式存储格式和很小的额外开销来存储嵌套的结构。有了这种技术即使是嵌套的字段在读取时也不需求牵扯到其他字段从而带来了性能上的极大提升。
Parquet的另一个特点是有很多工具都可以支持这种格式。作为Parquet的缔造者Twitter和Cloudera的工程师们希望在尝试使用新工具来处理现有的数据时能够更加简化。为了达成这一目标他们将该项目划分为两个部分其一是以语言无关的方式来定义文件格式的Parquet规范即Parquet-format另一部分是不同语言Java和C的规范实现以便人们能够使用多种工具读/写Parquet文件。
事实上大部分大数据处理组件都支持Parquet格式包括MapReduce、Hive、Spark等。这种灵活性同样也延伸至内存中的表示法Java的实现并没有绑定某一种表示法因而可以使用Avro、Thrift等多种内存数据表示法来讲数据写入Parquet文件或者从Parquet文件中读取数据。 数据模型
Parquet 的原子类型
Parquet定义了少数几个原子数据类型
类型描述boolean二进制值int3232位有符号整数int6464位有符号整数int9696位有符号整数float单精度浮点数double双精度浮点数binary8位无符号字节序列fixed_len_byte_array固定数量的8位五符号字节
保存在Parquet文件中的数据通过模式进行描述模式的根为messagemessage中包含一组字段每个字段由一个重复数required,optional或repeated分别表示有且只有一次,0或1次,0或多次、一个数据类型、一个字段名称构成。下面是一个简单的气象记录的Parquet模式
message WhatherRecord {required int32 year;required int32 temperature;required binary stationId(UTF8);
}请注意Parquet的原子类型并不包括字符串类型。事实上Parquet定义了一些逻辑类型这些逻辑类型指出应当如何对原子类型进行解读从而使得序列化的表示即原子类型与特定于应用的语义即逻辑类型相互独立。可以通过UTF8注解的binary原子类型表示字符串类型。
Parquet 的逻辑类型
下表列出了Parquet定义的一些逻辑类型且每种逻辑类型都有一个具有代表性的模式范例。表中没有列出的类型包括有符号整数、无符号整数、其他一些日期或时间类型以及JSON和BSON文档类型。
逻辑类型注解描述模式示例UTF8由UTF-8字符组成的字符串可用于注解binarymessage m { required binary a (UTF8);}ENUM命名值的集合可用于注解binarymessage m { required binary a (ENUM);}DECIMAL(precisionscala)任意精度的有符号小数可用于注解int32、int64、binary或fixed_len_byte_arraymessage m { required int32 a (DECIMAL(5,2));}DATE不带时间的日期值可用于注解int32。用Unix元年以来的天数表示message m { required binary a (DATE);}LIST一组有序的值可用于注解groupmessage m { required group a (LIST){ repeated group list{ required int32 element; } }}MAP一组无序的键值对可用于注解groupmessage m { required group a (MAP){ repeated group k_v{ required binary key (UTF8); optional int32 value; } }}
Parquet 利用 group 类型来构造复杂类型它可以增加一级嵌套。没有注解的group就是一个简单的嵌套记录。
可以用一种特殊的两级嵌套group结构构造list和map。list是通过LIST注解的group来表示其中又嵌套了一个重复的group命名为list元素字段包含在这个内层group中。一个32位整数的list由数据类型为int32且重复数为required必须出现一次的元素字段构成。对map来说外层的group a使用MAP注解嵌套了一个可重复的内层group命名为k_v,其中包含key和value两个字段。
嵌套编码
使用面向列式的存储格式时同一列数据连续存储。对于气象记录模式这种既无嵌套也无重复的扁平表而言非常简单。 由于每一列都含有相同数量的值因此可以直观地判断出每个值属于哪一行。
当過到嵌套和重复时比如map 模式事情一般会变得有些复杂因为还需要对嵌套的结构进行编码。有些列式存储格式通过將嵌套结构扁平化来回避这个问题使得只有位于最上层的列才能以列主column-major)方式存储例如 Hive 的RCFile 就采取了这种方式。这样具有嵌套列的 map 中的键和值将会交错存储也就是说虽然你只想读取键却不得不把值也读取到内存中。
Parquet 使用的是 Dremel 编码方法即模式中的每个原子类型的字段都单独存储为一列且每个值都要通过使用两个整数来对其结构进行编码这两个整数分别是列定义深度(definition level和列元素重复次数(repetition level)。这种编码方式的细节错综复杂不过你可以把列定义深度和列元素重复次数的存储想像成类似于用一个位字段来为扁平记录的空值进行编码而非空值则一个紧挨一个地存储。
这种编码方式带来的好处是对任意一列(即使是嵌套列)数据的读取都不需要涉及到其他列。例如在读取Parquet 的map 键-值对中的键时不需要访问任何值从而使其性能得到显著提升尤其是当值非常大的时候比如包含很多字段的嵌套记录。
Parquet文件格式
Parauet 文件由一个文件头(header)、一个或多个紧随其后的文件块(block)以及一个用于结尾的文件尾(footer)构成。文件头中仅包含一个称为 PAR1 的 4 字节数字(Magic Number)它用来识别整个 Parquet 文件格式。文件的所有元数据都被保存在文件尾中。文件尾中的元数据包括文件格式的版本信息、模式信息、额外的健值对以及所有块的元数据信息。文件尾的最后两个字段分别是一个 4 字节字段(其中包含了文件尾中元数据长度的编码)和一个 PAR1与文件头中的相同)。
由于元数据保存在文件尾中因此在读 Parquet 文件时首先要做的就是找到文件的结尾然后(减去 8个字节)读取文件尾中的元数据长度并根据元数据长度逆向读取文件尾中的元数据。顺序文件和 Avro 数据文件都是把元数据保存在文件头中并且使用 sync marker 来分割文件块而 Parquet 文件则不同由于文件块之间的边界信息被保存在文件尾的元数据中因此Parquet 文件不需要使用 sync marker。这种做法之所以可行正是因为元数据要等到最后才写人此时所有文件块都已写完只要文件没有关闭writer 就能在内存中保留这些文件块的边界位置。综上所述由于通过读取文件尾可以定位文件块因此Parquet 文件是可分割 且可并行处理的(例如通过 MapReduce 处理。
Parquet 文件中的每个文件块负责存储一个行组(row group)行组由列块(column chunk)构成且一个列块负责存储一列数据。每个列块中的数据以页(page)为单位存储如图所示。 由于每页所包含的值都来自于同一列因此极有可能这些值之间的差别并不大那么使用页作为压缩单位是非常合适的。初级压缩来自编码方式最简单的编码方式是无格式编码(plain encoding)即原封不动地存储一个值(例如使用 4 宇节的小端字节表示法来存储 int32 类型然而这种编码方式并没有提供任何程度的压缩。
Parquet 会使用一些带有压缩效果的编码方式包括差分编码(保存值与值之间的差)、游程长度编码(将一连串相同的值编码为一个值以及重复次数、字典编码(创建一个字典对字典本身进行编码然后使用代表字典索引的一个整数来表示值)。在大多数情况下Parquet 还会使用其他一些技术比如位紧缩法 (bit packing)它將多个较小的值保存在一个字节中以节省空间。
在写文件时Parquet 会根据列的类型自动选择适当的编码方式。例如在保存布尔类型时Parquet 会结合游程长度编码与位紧缩法。大部分数据类型的默认编码方式是字典编码但如果字典太大就要退回到无格式编码。触发退回的阈值称为字典页大小(dictionary page size)其默认值等于页的大小(因此倘若使用字典编码那么这个字典页不得超过一页的范围)。请注意实际采用的编码方式保存在文件的元数据中这样才能确保reader 在读取数据时使用正确的编码方式。 除编码外还可以以页为单位利用标准压缩算法对编码后的数据进行第二次压缩。Parquet 的默认设置是不使用任何压缩算法但它可以支持 Snappy、gzip 和LZ0 等压缩工具。
对于嵌套数据来说每一页还需要存储该页所包含的值的列定义深度和列元素重复次数。由于这两个数都是很小的整数(最大值取快于模式指定的嵌套深度)因此使用位紧缩法与游程长度编码可以非常有效地进行编码。