学习笔记(3):海量数据的交互式分析工具-Dremel

产生背景

Google公开了MapReduce计算框架之后,由于其强大的数据分析和处理能力,很快被视为数据分析的一个实际标准,各种围绕MapReduce框架的开发层出不穷。但互联网的发展,数据种类和应用需求呈现出爆炸式增长。MapReduce作为一种面向批处理的框架,在很多领域不再实用了。对此出现了两种思路,一种是对MapReduce进行改造,使其除了能进行批处理外还能进行其他类型的数据能力,比如处理流数据。另一种思路就是完全抛开MapReduce,根据具体的应用重新进行架构。很明显后一种思路对问题的解决更彻底,于是Google开发了Dremel系统。

Dremel的基本概述

Dremel是一款海量数据交互式分析工具。
Dremel和MapReduce并不是互相替代,而是相互补充的技术。在不同的应用场景下各有其用武之地。
Dremel系统有下面几个主要的特点:

  • List itemDremel是一个大规模系统。
  • Dremel是MR交互式查询能力不足的补充。
  • Dremel的数据模型是嵌套的。
  • Dremel中的数据是用列式存储的。
  • Dremel结合了Web搜索 和并行DBMS的技术

要注意的是:Dremel并不开源,但是Google利用Dremel向外界用户提供BigQuery服务,可以通过体验BigQuery服务来感受Dremel的强大功能

数据模型

dremel的数据模型的抽象语法如下:

技术支撑
Google的数据平台需要满足通用性,不同平台之间能够很好地实现数据的交互处理
  • 统一的存储平台:实现高效的数据存储,Dremel使用的底层数据存储平台是GFS
  • 统一的数据存储格式:存储的数据才可以被不同的平台所使用
面向记录和面向列的存储
- Google的Dremel是第一个在嵌套数据模型基础上实现列存储的系统
![在这里插入图片描述](https://img-blog.csdnimg.cn/27436ce7ce8441b89c04bc1969bccb51.png)
  • 处理时只需要使用涉及的列数据
  • 列存储更利于数据的压缩
嵌套模型的形式化定义

在这里插入图片描述
字符t是一个数据类型的定义,可以是原子类型,也可以是记录类型,Ai代表该τ的命名,即Ai就是某个t类型的变量

其中t可以是一个基本类型或者组合类型。其中基本类型可以是integer,float和string。组合类型可以是若干个基本类型拼凑。星号 (*)指的是任何类型都可以重复,就是数组一样。问号(?)指的是任意类型都是可以是可选的。简单来说,除了没有Map外,和一个Json几乎没有区别。

嵌套结构的模式和实例

下图是例子,Schema定义了一个组合类型Document.有一个必选列DocId,可选列Links,还有一个数组列Name。可以用Name.Language.Code来表示Code列。
在这里插入图片描述
该图是一个记录型文档的实例。其中,右上角的板块是该文档的模式定义。而r1和r2则是符合该模式的两条记录。r1和r2中的属性是通过完整路径表示的,比如:Name.Language.Code。
上图所示的嵌套式数据模型更具体的语言和处理平台无关,利用该数据模型可以使用Java和C++来处理数据,甚至可以用Java编写的MapReduce直接处理C++语言产生的数据集具有跨平台的优良特征。

上图,是一个示例数据的抽象的模型;下图是这份数据在Dremel实际的存储的格式。
在这里插入图片描述
如果是关系型数据,而不是嵌套的结构。存储的时候,我们可以将每一列的值直接排列下来,不用引入其他的概念,也不会丢失数据。对于嵌套的结构,我们 还需要两个变量R (Repetition Level) ,D (Definition Level) 才能存储其完整的信息。

Repetition Level是记录该列的值是在哪一个级别上重复的。举个例子说明:对于Name.Language.Code 我们一共有三条非Null的记录。

第一个是”en-us”,出现在第一个Name的第一个Lanuage的第一个Code里面。在此之前,这三个元素是没有重复过的,都是第一个。所以其R为0。
第二个是”en”,出现在下一个Lanuage里面。也就是说Lanague是重复的元素。Name.Language.Code中Lanague排第二个,所以其R为2.
第三个是”en-gb”,出现在下一个Name中,Name是重复元素,排第一个,所以其R为1。
我们可以想象,将所有的没有值的列,设值为NULL。如果是数组列,我们也想象有一个NULL值。有了Repetition Level,我们就可以很好的用列表示嵌套的结构了。但是还有一点不足。就是还需要表示一个数组是不是我们想象出来的。

Definition Level 是定义的深度,用来记录该列是否是”想象”出来的。所以对于非NULL的记录,是没有意义的,其值必然为相同。同样举个例子。例如Name.Language.Country,

  • 第一个”us”是在R1里面,其中Name,Language,Country是有定义的。所以D为3。
  • 第二个”NULL”也是在R1的里面,其中Name,Language是有定义的,其他是想象的。所以D为2。
  • 第三个”NULL”还是在R1的里面,其中Name是有定义的,其他是想象的。所以D为1。
  • 第四个”gb”是在R1里面,其中Name,Language,Country是有定义的。所以D为3。

就是这样,如果路径中有required,可以将其减去,因为required必然会define,记录其数量没有意义。
理解了如何存储这种嵌套结构。写没有难度。读的时候,我们只读其中部分字段,来构建部分的数据模型。例如,只读取DocID和 Name.Language.Country。我们可以同时扫描两个字段,先扫描DocID。记录下第一个,然后发现下一个DocID的R是0;于是该读 Name.Language.Country,如果下一个R是1或者2就继续读,如果是0就开始读下一个DocID。
在这里插入图片描述
下图展示了一个更为复杂的读取的状态机示例。在读取过程中使用了Definition Level来快速Jump,提升性能。
在这里插入图片描述
到此为止,我们已经知道了Dremel的数据结构。就像其他数据分析系统一样,数据结构确定下来,功能就决定了一大半。对于Dremel的数据查询,必然是“全表扫描”,但由于其巧妙的列存储设计,良好的数据模型设计可以回避掉大部分Join需求和扫描最少的列。

Google Dremel查询方式

Dremel可以使用一种SQL-like的语法查询嵌套数据。由于Dremel的数据是只读的,并且会密集的发起多次类似的请求。所以可以保留上次请求的信息,还优化下次请求的explain过程。那又是如何explain的呢?
在这里插入图片描述

这是一个树状架构。当Client发其一个请求,根节点受到请求,根据metadata,将其分解到枝叶,直到到位于数据上面的叶子Server。他们扫描处理数据,又不断汇总到根节点。

举个例子:对于请求:

SELECT A, COUNT(B) FROM T GROUP BY A
根节点收到请求,会根据数据的分区请求,将请求变成可以拆分的样子。原来的请求会变为。

SELECT A, SUM© FROM (R1 UNION ALL … Rn) GROUP BY A
R1,…RN是T的分区计算出的结果集。越大的表有越多的分区,越多的分区可以越好的支持并发。

然后再将请求切分,发送到每个分区的叶子Server上面去,对于每个Server

Ri = SELECT A, COUNT(B) AS c FROM Ti GROUP BY A
结构集一定会比原始数据小很多,处理起来也更快。根服务器可以很快的将数据汇总。具体的聚合方式,可以使用现有的并行数据库技术。

Dremel是一个多用户的系统。切割分配任务的时候,还需要考虑用户优先级和负载均衡。对于大型系统,还需要考虑容错,如果一个叶子Server出现故障或变慢,不能让整个查询也受到明显影响。

通常情况下,每个计算节点,执行多个任务。例如,技巧中有3000个叶子Server,每个Server使用8个线程,有可以有24000个计算单 元。如果一张表可以划分为100000个区,就意味着大约每个计算单元需要计算5个区。这执行的过程中,如果某一个计算单元太忙,就会另外启一个来计算。 这个过程是动态分配的。

对于GFS这样的存储,一份数据一般有3份拷贝,计算单元很容易就能分配到数据所在的节点上,典型的情况可以到达95%的命中率。

Dremel还有一个配置,就是在执行查询的时候,可以指定扫描部分分区,比如可以扫描30%的分区,在使用的时候,相当于随机抽样,加快查询。

Google Dremel测试实验

  • Google的实验数据集规模

说了这么多,那么Dremel的实际性能如何呢?实验的数据源如下表示,大部分数据复制了3次,也有一个两次,每个表会有若干分区,每个分区的大小在100K到800K之间。假设压缩率是25%,并且计入复制3份的事实的话,T1的大小已经达到PB级别,这么小且巨量的分区,对于GFS的要求很高,现在的Hdfs稳定版恐怕受不了。接下来的测试会逐步揭示其是如何超过MR,并对性能作出分析。
在这里插入图片描述

  • 列存测试

我们继续进行列存测试。首先,我们测试看看列存的效果,对于T1表,1GB的数据大约有300K行,使用列存的话压缩后大约在375MB,这台机器磁盘的吞吐在70MB/s左右,这1GB的数据,就是我们的现在的测试数据源,测试环境是单机。
在这里插入图片描述
见上图。

  • 曲线A,是用列存读取数据并解压的耗时。
  • 曲线B是一条一条记录挨个读的时间。
  • 曲线C是在B的基础上,加上了反序列化的时间。
  • 曲线d,是按行存读并解压的耗时。
  • 曲线e加上了反序列化的时间。因为列很多,反序列化耗时超过了读并解压的50%。

从图上可以看出。如果需要读的列很少的话,列存的优势就会特别的明显。对于列的增加,产生的耗时也几乎是线性的。而一条一条该个读和反序列化的开销是很大的,几乎都在原来基础上增加了一倍。而按行读,列数的增加没有影响,因为一次性读了全部列。

  • Dremel和MapReduce的对比测试

MR和Dremel最大的区别在于行存和列存。如果不能击败MapReduce,Remel就没有意义了。使用最常见的WordCount测试,计算这个数据中Word的个数。

Q1: SELECT SUM(CountWords(txtField)) / COUNT(*) FROM T1
在这里插入图片描述
上图是测试的结果。使用了两个MR任务。这两个任务和Dremel一样都运行在3000个节点上面。如果使用列存,Dremel的按列读的MR只需 要读0.5TB的数据,而按行存需要读87TB。 MR提供了一个方便有效的途经来讲按行数据转换成按列的数据。Dremel可以方便的导入MapReduce的处理结果。

MR从面向记录转换到列状存储后性能提升了一个数量级(从小时到分钟),而使用Dremel则又提升了一个数量级
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值