首页 > 代码库 > The striping and assembly algorithms from the Dremel paper( from github, project parquet-mr )
The striping and assembly algorithms from the Dremel paper( from github, project parquet-mr )
为了理解Dremel论文中给出的案例,笔者觉得对定义级别和重复级别这两个概念进行注释加强理解是有必要的,具体可以看Dremel那篇论文的图2和图3。
柱状数据的嵌套模式:
论文使用了以下的模型:
message Document {
required int64 DocId;
optional group Links {
repeated int64 Backward;
repeated int64 Forward; }
repeated group Name {
repeated group Language {
required string Code;
optional string Country; }
optional string Url; }}
这个模型可以被看作是一颗树,它的叶节点都是原子类型。一个叶节点对应开辟一个列。所以从这个例子我们可以得到6个列:
DocId
Links.Backward
Links.Forward
Name.Language.Code
Name.Language.Country
Name.Url
因为这其中有些字段是重复字端,我们需要一些另外的信息和数据一起存储以便日后重组这些记录。这就是重复级别和定义级别存在的意义了。
拆成列算法:
你可以把序列化一个记录看成是深度优先遍历一棵树。从记录的根节点开始,然后是第一个儿子节点直到你到达叶节点。孩子节点不是一个不同的字段就是一个重复字段的一个不同值。当访问到叶子节点时,写出相应列里带有最大定义级别(这意味着这些字段是被定义的)的节点值和它现有的重复级别(我们开始访问的根节点的重复级别为0)。当我们到达一个空字段,节点值和最后定义的在该节点之后的所有同类型的节点的定义级别都不会被写入(现有的重复级别也是如此,不被写入)。当回溯到树的上一个节点寻找另外的树枝继续遍历时,当前的重复级别为最后遇到的那个重复级别。
当一个字段并没有被定义时,它的定义级别会比该字段的定义级别小。
记录重组:
你想重构记录但只需要访问这些字段中的一个。重组记录就是把上文提到的树的遍历重做了一遍。选择你需要的字段并按如下方式重构:当一个节点的定义级别比最大的定义级别小,停止这一级的继续遍历;一旦访问到了叶子节点记录下它的重复级别同时开始从那个地方往下继续访问下一个值。
重复级别和定义级别:
关于级别们的详细阐释可以在这个blog里找到:https://blog.twitter.com/2013/dremel-made-simple-with-parquet【不翻墙打不开】
理解这些例子很重要的一点是需要知道并不是树的每一个节点都需要定义级别或是重复级别。只有重复字段需要重复级别,只有那些选择性存在的字段会需要定义级别。因为这些级别值都很小所以它们可以被很高效地压缩。
必须存在的那些字段(requiredfields)永远都是被存在的并且不需要定义级别,非重复字段的字段不需要重复级别。
下图是论文中案例的结构模型注释,帮助大家更好地理解:
因为定义级别和重复级别被数据模型的深度所限制,所以它们都是很小的整数。用几个bit就可以存储。一个只有必须字段的扁平化的数据结构不需要任何多余的bits来存储这些永远会为0的级别。一个值为NULL并且定义了的可选字段需要一个额外的bit来存储0。NULL值本身是不需要被专门存储的因为定义级别已经包含了这一信息。
一步一步计算:
R1
DocId:10
Links
Forward:20
Forward:40
Forward:60
Name
Language
Code:‘en-us‘
Country:‘us‘
Language
Code:‘en‘
Url:‘http://A‘
Name
Url:‘http://B‘
Name
Language
Code:‘en-gb‘
Country:‘gb‘
输出:
R = 0(当前重复级别)
DocId:10, R:0, D:0
Links.Backward:NULL, R:0, D:1 (定义了但没有值,所以D<2)
Links.Forward:20, R:0, D:2
R = 1(我们正在重复级别为1的‘Links.Forward‘)
Links.Forward:40, R:1, D:2
R = 1(我们又重复了一遍级别为1的‘Links.Forward‘)
Links.Forward:60, R:1, D:2
Back tothe root level: R=0
Name.Language.Code:en-us, R:0, D:2
Name.Language.Country:us, R:0, D:3
R = 2(我们在重复级别为2的‘Name.Language‘)
Name.Language.Code:en, R:2, D:2
Name.Language.Country:NULL, R:2, D:2 (没有值被定义D< 3)
Name.Url:http://A, R:0, D:2
R = 1(我们在重复级别为1的‘Name‘)
Name.Language.Code:NULL, R:1, D:1 (只有Name被定义,所以D= 1)
Name.Language.Country:NULL, R:1, D:1
Name.Url:http://B, R:1, D:2
R = 1(我们在重复级别为1的Name)
Name.Language.Code:en-gb, R:1, D:2
Name.Language.Country:gb, R:1, D:3
Name.Url:NULL, R:1, D:1
R2:
DocId:20
Links
Backward:10
Backward:30
Forward: 80
Name
Url:‘http://C‘
输出:
DocId:20, R:0, D:0
Links.Backward:10, R:0, D:2
Links.Backward:30, R:1, D:2
Links.Forward:80, R:0, D:2
Name.Language.Code:NULL, R:0, D:1
Name.Language.Country:NULL, R:0, D:1
Name.Url:http://C, R:0, D:2