parquet与avro嵌套列存结构比较

时间:2024-05-19 12:44:45

paruet列存文件结构图:

parquet与avro嵌套列存结构比较parquet与avro嵌套列存结构比较

如上图,文件由一个或者多个行组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])。