paruet列存文件结构图:
如上图,文件由一个或者多个行组Row Group组成,每个行组由多个列组成,每个列由多个页面组成,页面由其头部和Repetition levels、Definition levels和列的值values组成。
以上结构可以在加载时追加record,record首先存在内存中,当内存不够用时,将此时内存中所有的record组成一个行组Row Group输出到文件中,内存清空。
而在avro列存中没有行组的概念,只能将所有record全部加载到内存中,然后一起写到disk中。
parquet是可以存储嵌套属性的,实现这一点,Repetition levels和Definition levels很重要。以下是对这两个值的介绍。
Definition levels表示,在这条嵌套路径上repeated field和optional field不为null的个数,不包括required field。
Repetition levels表示,这个值是在嵌套路径上的哪个级别新建,针对repeated field。
以下是一个嵌套的avro中schema例子,record A嵌套record B数组嵌套record C数组。
{"name": "A", "type": "record",
"fields": [
{"name": "a", "type": "string"},
{"name": "nestArray1",
"type": {"type": "array",
"items": {"type": "record", "name": "B",
"fields":[
{"name": "b", "type": "string"},
{"name": "nestArray2",
"type": {"type": "array",
"items":{"type": "record", "name": "C",
"fields":[
{"name": "c", "type": "string"}
]
}
}
}
]
}
}
}
]}
转化成parquet schema变成
message parquet.avro.A {
required string a;
required group nestArray1(LIST) {
repeated group B {
required string b;
required group nestArray2(LIST) {
repeated group C {
required string c;
}
}
}
}
}
假设有3个上述schema的A record要存:
A{
a: "a1",
nestArray1: {
B: {b: "b11",
nestArray2:{
C: {c: "c111"}
C: {c: "c112"}}},
B: {b: "b12",
nestArray2:{
C: {c: "c121"}}}
}
}
A{
a: "a2",
nestArray1: {
B: {b: "b21",
nestArray2:{
C: {c: "c211"}}},
B: {b: "b22",
nestArray2: null}
}
}
A{
a: "a3",
nestArray1: null
}
parquet存储
a列存储:
V |
"a1" |
"a2" |
"a3" |
b列存储:
R | D | V |
0 | 1 | "b11" |
1 | 1 | "b12" |
0 | 1 | "b21" |
1 | 1 | "b22" |
0 | 0 | null |
对b列来说,路径上只有一个repeated field,且没有optional field,所以R和D都是最大为1,最小为0。
R为0表示该值是属于一个新的A record,为1表示是属于一个新的B record。
D为0表示从nestArray1开始为null,为1表示不为null。
c列存储:
R | D | V |
0 | 2 | "c111" |
2 | 2 | "c112" |
1 | 2 | "c121" |
0 | 2 | "c211" |
1 | 1 | null |
0 | 0 | null |
对c列来说,路径上有2个repeated field,没有optional field,所以R和D都是最大为2,最小为0。
R为0表示该值是属于一个新的A record,为1表示是属于一个新的B record,为2表示属于一个新的C record。
D为0表示从nestArray1开始为null,为1表示从nestArray2开始为null,为2表示不为null。
avro存储:
对于上述嵌套schema,对比于parquet列存,avro列存会多出2个array列,所以一共有5列:a, nestArray1, b, nestArray2, c,array列存储的是嵌套行数
a与nestArray1列存储
a | nestArray1 |
"a1" | 2 |
"a2" | 2 |
"a3" | 0 |
nestArray1列的两个2表示"a1", "a2"分别对应两个B record,0表示"a3"没有嵌套的B record。
b与nestArray2列存储
b | nestArray2 |
"b11" | 2 |
"b12" | 1 |
"b21" | 1 |
"b22" | 0 |
nestArray1列的2表示"b11"有两个嵌套的C record,两个1表示"b12", "b21"分别对应1个C record,0表示"b22"没有嵌套的C record。
c列存储
c |
"c111" |
"c112" |
"c121" |
"c211" |
另外,这种A|B|C嵌套存储,parquet可以单独读取B、C record中的列,
但avro列存不行,读取B时必须包含对应的nestArray1,读取C record时必须包含对应的nestArray1,即一定要从A record中的field开始读([a, nestArray1])。