parquet列式文件实战
前言
列式文件,顾名思义就是按列存储到文件,和行式存储文件对应。保证了一列在一个文件中是连续的。下面从parquet常见术语,核心schema和文件结构来深入理解。最后通过java api完成write和read。
术语
block
parquet层面和row group是一个意思
row group
逻辑概念,用于对row进行分区。由数据集中每个column的column chunk组成。是读写过程中的缓存单元,一般在hdfs上推荐一个block为1GB,一个HDFS文件1个bolock
column chunk
某个column的所有数据被称为column chunk,存在与row group,并保证在文件中是连续的
page
多个column chunk之间用page分开,也就是说一个page只会包含一个column的数据,一个page是一个独立的单元(可以被编码或者压缩)
dictionary page
每个page之前都可以选择是否需要dictionary page。dictionary page记录了该page所有不同的值。这可以增强处理速度提高压缩率。
总结
一个文件由多个row group组成,一个row group包括了多个column chunk,一个column chunck就是某个column的所有数据集, 被分割成多个page,一个page是最小的处理单元,可以被编码或者压缩。
schema
每种文件都有自己特有的规则,像csv文件,是用分隔符分隔开的一个个列。parquet文件也有自己独特的schema格式。
这是一个parquet文件的schema例子,对应的api是MessageType
message person{
required binary name (UTF8);
required int age;
repeated group family{
required binary father (UTF8);
required binary mother (UTF8);
optional binary sister (UTF8);
}
}
message
固定声明,就像结构体中的struct一样。
person
message name,可以粗暴的理解为表名,因为里面都是field。
optional,required,repeated
这是三种field的关键字,分别表示可选,必选,可重复选
可选和必选类似数据库中的nullable,可重复选是为了支持复杂的嵌套结构。
field类型
目前parquet支持int32,int64,int96(有些系统会把时间戳存成int96如老版本hive),float,double,boolean,binary,fixed_len_byte_array。
参考类org.apache.parquet.schema. PrimitiveType.PrimitiveTypeName
UTF8
field的原始类型(Original Type),可以辅助field的type进行细粒度的类型判断。
参考类 org.apache.parquet.schema. OriginalType
group
嵌套结构声明,类似json对象
schema&数据
schema有了,那如何把schema和数据关联起来,也就是说可以通过schema构建或者解析出相应的数据。那就引出了嵌套关系,definition level和repetitional level。用于定位数据到底出现在嵌套中(如果有嵌套的话)的哪一层。值得注意的是,嵌套关系是针对列而言的,不同列有各自的嵌套关系。
definition level
optional字段定位,如果实际没有数据就为0,有数据就为1。涉及到嵌套optional,那么可以这么理解,如果从某一层开始没有该数据,那么该层之前肯定是有数据的,该层之后肯定没有数据。举个简单的例子
message ExampleDefinitionLevel {
optional group a {
optional group b {
optional string c;
}
}
}
这个schema对应的definition level所有的可能性如表所示

repetition level
repeated字段定位,如果在嵌套中某一层出现了值,那么就记录该层。那一个例子来说:
message AddressBook {
required string owner;
repeated string ownerPhoneNumbers;
repeated group contacts {
required string name;
optional string phoneNumber;
}
}
针对不同的列,defnition level和repetition level的最大值如表

文件结构
结构图

详细
一个parquet文件由3部分组成,header,blocks,footer。类似一般文档中的页眉,正文,页脚。
header
只包含4个字节的魔数,PAR1
blocks
block定义参考“术语”
footer
记录了该parquet文件正文所有metadata,
文件物理格式
通过 cat -v 查看一个parquet,会看到很多的non-printable字符,比如:^U^@^U^P^U^P,^U^B^U^@^
这些字符其实是可以和ascii互相映射,比如^@就是ascii中的0,详细可以看这篇文档
https://docstore.mik.ua/orelly/unix/upt/ch25_07.htm
其实就是八进制的ascii,小于100的+100,大于100的减100。
所有的列,包括嵌套结构,例如test.c1和test.c2属于两个列,都是连续存储在parquet文件中。
参考资料
// twitter对parquet的概述
// parquet的github
https://github.com/apache/parquet-format
// 很详细的parquet文件解析
http://www.infoq.com/cn/articles/in-depth-analysis-of-parquet-column-storage-format
coding
public static MessageType getMessageTypeFromCode(){
MessageType messageType =
Types.buildMessage()
.required(PrimitiveType.PrimitiveTypeName.BINARY).as(OriginalType.UTF8).named("id")
.required(PrimitiveType.PrimitiveTypeName.BINARY).as(OriginalType.UTF8).named("name")
.required(PrimitiveType.PrimitiveTypeName.INT32).named("age")
.requiredGroup()
.required(PrimitiveType.PrimitiveTypeName.BINARY).as(OriginalType.UTF8).named("test1")
.required(PrimitiveType.PrimitiveTypeName.BINARY).as(OriginalType.UTF8).named("test2")
.named("group1")
.named("trigger");
return messageType;
}
public static void writeParquet(String name){
// 1. 声明parquet的messageType
MessageType messageType = getMessageTypeFromCode();
System.out.println(messageType.toString());
// 2. 声明parquetWriter
Path path = new Path("/tmp/etl/"+ name);
Configuration configuration = new Configuration();
GroupWriteSupport.setSchema(messageType, configuration);
GroupWriteSupport writeSupport = new GroupWriteSupport();
// 3. 写数据
ParquetWriter<Group> writer = null;
try {
writer = new ParquetWriter<Group>(path,
ParquetFileWriter.Mode.CREATE,
writeSupport,
CompressionCodecName.UNCOMPRESSED,
128*1024*1024,
5*1024*1024,
5*1024*1024,
ParquetWriter.DEFAULT_IS_DICTIONARY_ENABLED,
ParquetWriter.DEFAULT_IS_VALIDATING_ENABLED,
ParquetWriter.DEFAULT_WRITER_VERSION,
configuration);
Random random = new Random();
for(int i=0; i<10; i++){
// 4. 构建parquet数据,封装成group
Group group = new SimpleGroupFactory(messageType).newGroup();
group.append("name", i+"@qq.com")
.append("id",i+"@id")
.append("age",i)
.addGroup("group1")
.append("test1", "test1"+i)
.append("test2","test2"+i);
writer.write(group);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(writer != null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void readParquet(String name){
// 1. 声明readSupport
GroupReadSupport groupReadSupport = new GroupReadSupport();
Path path = new Path("/tmp/etl/"+name);
// 2.通过parquetReader读文件
ParquetReader<Group>reader = null;
try {
reader = ParquetReader.builder(groupReadSupport, path).build();
Group group = null;
while ((group = reader.read()) != null){
System.out.println(group);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
parquet列式文件实战的更多相关文章
- parquet列式文件实战(未完,待续)
parquet列式文件实战 parquet code demo http://www.programcreek.com/java-api-examples/index.php?source_dir=h ...
- 【转】深入分析 Parquet 列式存储格式
Parquet 是面向分析型业务的列式存储格式,由 Twitter 和 Cloudera 合作开发,2015 年 5 月从 Apache 的孵化器里毕业成为 Apache 顶级项目,最新的版本是 1. ...
- 深入分析Parquet列式存储格式【转】
Parquet是面向分析型业务的列式存储格式,由Twitter和Cloudera合作开发,2015年5月从Apache的孵化器里毕业成为Apache顶级项目,最新的版本是1.8.0. 列式存储 列式存 ...
- Parquet列式存储格式
Parquet是面向分析型业务的列式存储格式,由Twitter和Cloudera合作开发,2015年5月从Apache的孵化器里毕业成为Apache顶级项目,最新的版本是1.8.0. 列式存储 列式存 ...
- 深入分析Parquet列式存储格式
Parquet是面向分析型业务的列式存储格式,由Twitter和Cloudera合作开发,2015年5月从Apache的孵化器里毕业成为Apache顶级项目,最新的版本是1.8.0. 列式存储 列式存 ...
- Parquet 列式存储格式
Parquet 列式存储格式 参考文章: https://blog.csdn.net/kangkangwanwan/article/details/78656940 http://parquet.ap ...
- Hadoop IO基于文件的数据结构详解【列式和行式数据结构的存储策略】
Charles所有关于hadoop的文章参考自hadoop权威指南第四版预览版 大家可以去safari免费阅读其英文预览版.本人也上传了PDF版本在我的资源中可以免费下载,不需要C币,点击这里下载. ...
- Parquet与ORC:高性能列式存储格式(收藏)
背景 随着大数据时代的到来,越来越多的数据流向了Hadoop生态圈,同时对于能够快速的从TB甚至PB级别的数据中获取有价值的数据对于一个产品和公司来说更加重要,在Hadoop生态圈的快速发展过程中,涌 ...
- 开源列式存储引擎Parquet和ORC
转载自董的博客 相比传统的行式存储引擎,列式存储引擎具有更高的压缩比,更少的IO操作而备受青睐(注:列式存储不是万能高效的,很多场景下行式存储仍更加高效),尤其是在数据列(column)数很多,但每次 ...
随机推荐
- 解决Nginx+Tomcat时ContextPath不同的问题
1 问题描述 项目前端模板使用Thymeleaf,在对各种URL进行格式化输出时,都使用@{uri}代码.它会自动读取项目部署的虚拟路径,添加到URI的前端输出. 真实测试和生产环境中, ...
- 使用Java 8中的Stream
Stream是Java 8 提供的高效操作集合类(Collection)数据的API. 1. 从Iterator到Stream 有一个字符串的list,要统计其中长度大于7的字符串的数量,用迭代来实现 ...
- 主要讲下hack的兼容用法,比较浅,哈哈
hack是主要来处理IE的兼容,不同的IE,不同的兼容方式 /* 属性前缀法(即类内部Hack): *color:#000; *号对IE6,IE7都生效 +color:#555; ...
- web开发|如何选择合适的webui框架
在市场中很多人分不清框架和库的区别,部分只知道框架模糊的概念.所以在选择webUI框架的时候就会仁者见仁智者见智,会存在各抒己见也是很正常的,这里整体都叫框架吧,在市场中不断的淘汰与创新,主要以Vue ...
- PHP常用配置
Php配置文件:php.ini(使用‘;’表示注释) Php的配置项可以在配置文件中配置,也可以在脚本中使用ini_set()函数临时配置. 语言相关配置: 1. engine:设置PHP引擎是否可用 ...
- Leetcode题解(29)
93. Restore IP Addresses 题目 分析:多重循环,判断小数点合适的位置 代码如下(copy网上) class Solution { public: vector<strin ...
- ssh连接虚拟机失败解决办法
首先,你需要知道本机IP跟虚拟机IP,然后让两者互相ping一下,看能否ping通 让主机ping虚拟机 :ping (虚拟机ip) 出现如下: 表示主机可以ping通虚拟机 然后让虚拟机ping主机 ...
- JavaScript instanceof 运算符深入剖析【转载】
http://www.ibm.com/developerworks/cn/web/1306_jiangjj_jsinstanceof/ instanceof 运算符简介 在 JavaScript ...
- 拓扑排序 HDU - 5695
众所周知,度度熊喜欢各类体育活动. 今天,它终于当上了梦寐以求的体育课老师.第一次课上,它发现一个有趣的事情.在上课之前,所有同学要排成一列, 假设最开始每个人有一个唯一的ID,从1到NN,在排好队之 ...
- PHP--最常用--必背函数总结!php学习者收藏必备!
一.PHP系统函数 函数 功能 用法 var_dump() 打印变量结构信息,包括类型和值.数组将递归展开值 var_dump ( $arg1...); echo():是语法结构 输出一个或者多个字符 ...