Google Dremel和parquet的复杂嵌套数据结构表征方法解析

转载请注明出处。作者:archimekai
核心参考文献: Dremel: Interactive Analysis of Web-Scale Datasets

文章目录

  • 引言
  • 复杂嵌套数据结构的无损表征问题
  • Dremel论文中提出的表征方法
  • parquet
  • 备注

引言

Dremel是Google的交互式分析系统。Google大量采用protobuf格式,因此Dremel必须支持protobuf这种复杂嵌套格式的分析。众多工作已经论证了列式存储格式对分析系统(AP, analytical processing)的重要性,因此Dremel需要把复杂嵌套格式存储为列存。本文接下来重点分析Dremel是如何把复杂嵌套格式转换为每一列单独存储的。

我们的目标是:没有蛀牙(划掉)把相同字段的所有值连续存储。因此需要设计一种能够适用于任意protobuf/json格式的,无损的数据表征方式。

复杂嵌套数据结构的无损表征问题

不同的数据,其表征难度是不同的。最简单的是完全没有嵌套,平铺直叙的数据。这种数据只要将每一个字段的名称取出,放在数据库中作为一列即可。
数据样例如下:

{"Code": "en-us", "Country": "us"}

一种表征方式如下:

CodeCountry
en-usus

如果数据中有嵌套,但是没有列表,也比较简单。只需要用某个分隔符(例如 . )把嵌套的字段名称连接起来即可。以下面的数据为例:

{"DocId": 10, "Links": {"Forward": 20}}

其一种表征方式如下:

DocIdLinks.Forward
1020

如果数据中存在列表,就比较复杂了。这里的难点在于,列表的长度无法提前预知,所以一般不能把列表中的每一个元素都作为一列存储。例如

{"Links": {"Backward": [10,30], "Forward": [80]}}

不能表征为

Links.Backward[0]Links.Backward[1]Links.Forward[0]
103080

如果将来Backward中有10000个元素就很难处理。

这个问题其实有一个简单场景,即列表中的元素都是相同的基本数据类型。这里的基本数据类型可以简单理解为数据库有原生数组类型支持的数据类型,例如 int ( int[] ),float ( float[] )等,因此,上述数据可以表征为下表。Hologres就是采用了这样的方案。

Links.BackwardLinks.Forward
[10.30][80]

通过上面的方案,笔者认为已经可以搞定80%以上的场景了。但是如果列表中的元素不是基本数据类型,上面的的方案就又搞不定了。考虑下面的数据:

{"Name": [{"url": "http://C", "long_data": "data"}]}

在这个例子中,数组里面的元素是{"url": "http://C"},并不是一种基本数据类型,如何存储才能支撑对Name.url的高效查询呢?。除了上面的例子,还有更为复杂的数组套数组的例子:

{"Name": [{"Language":[{"Code": "en-us", "Country": "us"}, {"Code": "en"}]}]}

那么,有没有一种方法可以一劳永逸地搞定所有protobuf/强类型json(强类型json指每个字段的类型是确定的)能够表达的所有数据呢?还真有,这就是Google的天才工程师们在Dremel论文中提出的方案。下面笔者重点介绍这个方案。

Dremel论文中提出的表征方法

还是看上面数组套数组的例子,主要的困难点是,读取数据时如何确定内层元素(例如{"Code": "en"})的位置?这需要准确表达内层元素在当前数组(也即Language)和父数组(也即Name)中的位置,才能在读取时把数据结构还原回来。这提示我们,至少需要增加两个位置信息才行。
Google的工程师们也是这么想的,他们定义了两个变量来表示上述信息,这两个变量也是理解Dremel数据表征算法的关键:

  • 第一个变量是repetition level,简称r,其表示对于给定的字段,其当前在哪一个级别重复。这个概念比较抽象,请结合下面的例子仔细阅读备注进行理解。
  • 第二个变量是definition level,简称d,其表示对于给定的字段,其前面有多少个字段是存在的。例如,r3中的DocId,d=0,也即DocId更上层没有字段了,r3中的Links.Forward字段,d=1,也即Links这个字段是存在的。

可以看到,r和d并非直接表示当前数组和父数组的位置,而是以一种类似增量(在当前级别重复还是在哪个级别重复)的方式进行编码。

一个加料版的例子(r3,基于论文中的r1修改而来)。数据schema如下

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; }} 

数据如下(简称r3)

{"DocId": 10, "Links": {"Forward": [20,40,60]}, "Name": [{"Language": [{"Code": "en-us", "Country": "us"},{"Code": "en"},{"Code": "zh-cn", "Country": "cn"}]"Url": "http://A"},{"Url": "http://B"},{"Language": [{"Code": "en-us", "Country": "gb"}]}]
}

数据r3中的Name.Language.Country字段编码后如下。

valuerd备注
us02r=0表示r3这篇文档的第一个Country字段,d=2表示Name, Language两个字段都是存在的
null22r=2表示Language字段在第二级,也即Language数组这一级重复,null表示Country字段实际上未在Language这个数组的第二个元素{"Code": "en"}中出现。注意null在dremel的编码方式中是必须的
cn22r=2表示Language字段在第二级,也即Language数组这一级重复,出现在Language这个数组的第三个元素
null11r=1表示Language字段在第一级,也即Name数组这一级重复,null表示Country字段实际上为在Name这个数组的第二个元素{"Url": "http://B"}中出现。结合d=1可知,Name.Language.Country中只有头一个字段,也即Name存在,Language字段不存在。
gb12r=1表示Language字段在第一级,也即Name数组这一级重复,出现在Name数组的第三个元素

到了这里,相信读者已基本理解了r和d的含义。笔者将Dremel论文中的原图贴在这里,大家应该可以看懂了。
Dremel论文原文中的例子

parquet

parquet中对于嵌套数据的数据和Google Dremel基本一致。本篇文章不详细展开

备注

  • 为了方便理解,上述描述中省略了一些不重要的细节,感兴趣的读者可自行阅读论文原文。
  • 本篇文章只介绍了最核心的数据结构表征方式,Dremel论文中还有关于如何将各个字段拆分为列,如何使用有限状态自动机真正生成r和d值的描述,以后有机会再介绍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/508344.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阅读笔记 | Transformers in Time Series: A Survey

阅读论文: Wen, Qingsong, et al. “Transformers in time series: A survey.” arXiv preprint arXiv:2202.07125 (2022). 这篇综述主要对基于Transformer的时序建模方法进行介绍。论文首先简单介绍了Transformer的基本原理,包括位置编码、多头注意力机…

01 MySQL之连接

1. 连接 1.0 基础认知 多表(主表)和一表(从表的区别): 多表一般是主表,一般存储主要数据,每个字段都可能存在重复值,没有主键,无法根据某个字段定位到准确的记录; 一表一般是从表,一般存储辅助数据&…

基础二分学习笔记

模板 : 个人倾向第一种 ; 整数二分 : 最大化查找 : 可行区域在左侧 : 查找最后一个<q的数的下标 : int find(int q){// 查找最后一个 < q 的下标 int l 0 , r n 1 ;while(l 1 < r){int mid l r >> 1 ;if(a[mid]<q) l mid ;else r mid ;}return…

如何限制一个账号只在一处登陆

大家好&#xff0c;我是广漂程序员DevinRock&#xff01; 1. 需求分析 前阵子&#xff0c;和问答群里一个前端朋友&#xff0c;随便唠了唠。期间他问了我一个问题&#xff0c;让我印象深刻。 他问的是&#xff0c;限制同一账号只能在一处设备上登录&#xff0c;是如何实现的…

第二篇【传奇开心果系列】Python的自动化办公库技术点案例示例:深度解读Pandas金融数据分析

传奇开心果博文系列 系列博文目录Python的自动化办公库技术点案例示例系列 博文目录前言一、Pandas 在金融数据分析中的常见用途和功能介绍二、金融数据清洗和准备示例代码三、金融数据索引和选择示例代码四、金融数据时间序列分析示例代码五、金融数据可视化示例代码六、金融数…

CUDA 中的线程组织

明朝那些事中有一句话&#xff1a;我之所以写徐霞客是想告诉你&#xff0c;所谓千秋霸业万古流芳&#xff0c;与一件事相比&#xff0c;其实都算不了什么&#xff0c;这件事情就是——用你喜欢的方式度过一生。 我们以最简单的 CUDA 程序&#xff1a;从 GPU 中输出 Hello World…

C++基于多设计模式下的同步异步日志系统day4

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C基于多设计模式下的同步&异步日志系统 &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 只要内容主要实现了同步日志消息…

Unreal触屏和鼠标控制旋转冲突问题

Unreal触屏和鼠标控制旋转冲突问题 鼠标控制摄像机旋转添加Input轴计算旋转角度通过轴事件控制旋转 问题和原因问题原因 解决办法增加触摸控制旋转代码触屏操作下屏蔽鼠标轴响应事件 鼠标控制摄像机旋转 通过Mouse X和Mouse Y控制摄像机旋转。 添加Input轴 计算旋转角度 通过…

外贸网站模板建站

测绘检测wordpress外贸主题 简洁实用的wordpress外贸主题&#xff0c;适合做测绘检测仪器设备的外贸公司使用。 https://www.jianzhanpress.com/?p5337 白马非马衣服WordPress外贸建站模板 白马非马服装行业wordpress外贸建站模板&#xff0c;适用于时间服装企业的官方网站…

利用redis实现秒杀功能

6、秒杀优化 这个是 图灵 的redis实战里面的一个案例 6.1 秒杀优化-异步秒杀思路 我们来回顾一下下单流程 当用户发起请求&#xff0c;此时会请求nginx&#xff0c;nginx会访问到tomcat&#xff0c;而tomcat中的程序&#xff0c;会进行串行操作&#xff0c;分成如下几个步骤…

求Sn=a+aa+aaa+aaaa+aaaaa的前n项之和

求Snaaaaaaaaaaaaaaa的前5项之和&#xff0c;其中a是一个数字&#xff0c; 例如&#xff1a;222222222222222 int main() {int a;scanf("%d", &a);int n;scanf("%d", &n);int sum 0;int tmp 0;for (int i 0; i < n; i){tmp tmp * 10 a;sum…

Wikidata数据结构及本地部署——支持SPARQL查询

Wikidata 本地部署 Github&#xff1a;https://github.com/NP-NET-research/wdel 欢迎来 star && fork && issue ~~ 有问题可以通过邮箱&#xff1a;xuzhengfei-emailqq.com 联系我~~ 文章目录 Wikidata 本地部署1、Wikidata 简介1.1 Wikidata 数据结构1.2 …