Apache Doris 物化视图介绍

目录

Apache Doris 物化视图介绍

改文章是基于Doris核心开发工程师缪翎在2020年7月线上meetup的PPT整理

1.Doris概述

首先 Doris 一个MPP架构的,分析型的数据库产品。对于PB数量级的,结构化数据,可以做到亚秒级查询响应。使用上兼容 MySQL 协议,语法是标准的 SQL。本身不依赖任何其他系统,相比hadoop生态产品更易于运维。

应用场景包括:固定历史报表xxx

用户的原始数据,比如日志,或者在事务数据库中的数据,经过流式系统或离线处理后,导入到Doris中,供上层的报表工具或者数据分析师查询使用。

img

##

2.Doris 数据模型介绍

在 DORIS 中,数据以表(Table)的形式进行逻辑上的描述。 一张表包括行(Row)和列(Column)。Row 即用户的一行数据。Column 用于描述一行数据中不同的字段。

Column 可以分为两大类:Key 和 Value。从业务角度看,Key 和 Value 可以分别对应维度列和指标列。

DORIS 的数据模型主要分为3类:

  • Duplicate 明细模型
  • Aggregate 聚合模型
  • Unique 唯一主键模型

2.1 聚合模型

聚合模型的特点就是将表中的列分为了key和value两种。Key就是数据的维度列,比如时间,地区等等。Value则是数据的指标列,比如点击量,花费等。每个指标列还会有自己的聚合函数,包括sum,min,max,bitmap_union 等等。数据会根据维度列进行分组,并对指标列进行聚合。

例如: 下表中同一天,同一个国家的相同广告的点击量数据就会相加求和后存储在Doris中。

image-20210807091258401

聚合模型的数据在Doris的3种机制下都会发生聚合:

首先是导入数据,原始数据在导入过程中,会根据表结构中的key进行分组,相同key的value会根据表中定义的aggregation function进行聚合。

例如:原始数据如左图,其中2019年12月31日广告1在美国的点击量数据有两条,都是5次点击,在导入完成后,这两次点击量就会求和最终Doris中存储的2019年12月31日广告1在美国的点击量为10。

image-20210807091454232

由于Doris采用的是MVCC 机制进行的并发控制。所以每一次新的导入都是一个新的版本。我们把这种版本成为 Singleton。

不断的导入新的数据后,尽管同一批次的数据在导入过程中已经发生了聚合,但不同版本之间的数据依旧存在维度列相同但是指标列并没有被聚合的情况。这时候就需要通过Compaction机制进行二次聚合。

Compaction的意思其实就是将不同版本的数据进行合并。他分为两个阶段,第一个阶段是

当Singleton的数据版本个数到达Doris设置的阈值时,就会触发Cumulative级别的compaction。这个级别的compaction会将一个区间段内的版本数据根据定义好的聚合函数进行再聚合。

例子:cumulatives 会将61-70的这10个singleton版本的数据合并成一个版本。这个版本的范围就是61~70。

经过Cumulative compaction后的数据已经合并多个区间内的版本,但并没有最终合并成一个版本。这时候就需要Base compaction来对已经完成cumulative compaction的版本做一个最终的合并。

例子:如图所示把从第0个版本到第60个版本直接合并成一个版本。而下一次的base就是把61-70这个已经完成cumulative合并的版本再进行base合并,生成一个0~70的最终版本。

image-20210807091712204

由于compaction是异步后台执行的,在用户查询之前数据还并未合并在同一个版本中,而是存在于多个版本中的。所以用户查询数据的时候,为了保证查询结果的正确性,Doris会把从0到当前最新版本的数据都读出来然后再做一次聚合,最后将结果返回给用户。

例如:有一个订单表,以订单id为维度列,订单的状态为指标列,且聚合函数为replace,也就是当订单id相同时,新订单状态覆盖旧的订单状态。

这时候版本0-30中订单1的状态可能是待付款,版本31-35中订单1的状态是已收货,最后一个版本36中订单1的状态是已完成。

那么用户在查询每个订单状态的时候,不同版本之间的数据就需要进行一个replace。取最后一个版本中的已完成状态作为查询的结果。

image-20210807091549971

2.2 ROLLUP

Rollup也就是上卷是一种在多维分析中比较常用的操作。也就是从细粒度的数据向高层的聚合。

Doris中,提供了在聚合模型上的构建Rollup功能,将数据根据更少的维度进行预聚合,本身在用户查询时才会进行聚合计算的数据,预先计算好,存储在doris中。从而达到提升用户粗粒度上的查询效率。

比如如果你的数据以站点流量数据为主,那么你创建按天维度聚合的Rollup,那么这个Rollup中的数据就是每天的站点流量统计。当然你也可以创建按更粗粒度的按月分组的Rollup。来提升不同粒度的查询。

Rollup还有一点好处在于,由于Doris具有在原始数据上实时计算的能力,因此不需要对所有维度的每个组合都创建rollup,尤其是在维度很多的情况下,可以获取一个存储空间和查询效率之间的平衡。

image-20210807091945385

创建Rollup的时候:首先你需要有一个聚合模型的base表,然后就可以取部分维度创建一个rollup表

例如:

在刚才的广告点击率的表上选取城市和广告id作为维度列构建一个Rollup表。那么不同时间的相同广告Id,就会再次进行聚合。比如12月31日和1月1日在美国对广告1进行的点击量就会相加得到40这个总和。

在导入新数据的过程中:为保证Rollup表和原始表的数据一致性。新增数据会同时导入Base表和Rollup表。对Rollup表的更新并且不是拿整个base数据重新构建一遍rollup,而是增量更新。

例如:

新增一条广告12月31日广告1在美国的点击量数据1,则base表中的点击量就会变成11,而rollup表中的点击量就会变成41。

查询的时候会根据查询需要的维度列以及聚合方式,自动匹配到最优的(一般就是聚合程度最高的)Rollup表。由于聚合数据已经提前计算好了,从来就能达到一个加快查询的功能。

例如:

如果想要分析每条广告在不同国家的点击量是,doris就会自动从Rollup表中读取数据。

image-20210807092336219

2.3 明细模型

明细模型是 DORIS 默认使用的数据模型。该数据模型不会对导入的数据进行任何处理。表中的数据即用户导入的原始数据。

image-20210807092447475

聚合模型的优点就在于:划分维护和指标列后,数据本身已经进行过预聚合,对于分析型查询效率提升明显。

但是聚合模型在某些用户场景下并不适用:

1.很多业务并没有聚合的需求,就是要存储原始的用户行为日志。

2.一些业务在初期还不能确认哪些是维度列,哪些是指标列

3.聚合模型本身更难理解,对新用户体验不好,比如一些查询结果和用户预期的不一致。

基于以上问题,我们就支持了明细数据模型:(点)

明细数据模型刚好和聚合模型相反,不区分维护和指标列,并不对导入的数据做任何聚合,每条原始数据都会保留在表中。

例如:表B中每个用户对每条广告的点击和消费行为都详细记录在表中。

明细模型就像Mysql中的表一样,优势就在于你可以详细追溯每个用户行为或订单详情。但劣势也很明显,分析型的查询效率不高。

3. Doris物化视图

image-20210807092621715

物化视图的出现主要是为了满足用户,既能对原始明细数据的任意维度分析,也能快速的对固定维度进行分析查询。

首先什么物化视图:定义上来说,就是包含了查询结果的数据库对象,可能是对远程数据的本地copy,也可能是一个表或多表join后结果的行或列的子集,也可能是聚合后的结果。说白了,就是预先存储查询结果的一种数据库对象。

在Doris中的物化视图,就是查询结果预先存储起来的特殊的表。

优势:

  1. 对于那些经常重复的使用相同的子查询结果的查询性能大幅提升
  2. Doris自动更新物化视图的数据,保证base 表和物化视图表的数据一致性。无需额外的维护成本
  3. 查询的时候也可以自动匹配最优的物化视图

3.1 创建物化视图

image-20210807092808907

首先你需要有一个base表,基于这个base表的数据提交一个创建物化视图的任务,任务中定义好物化视图的如何构建。然后Doris就会异步的执行创建物化视图的任务了。

创建物化视图表的语法和PostgreSQL,Oracle都是一致的。但是Doris目前创建物化视图只能在单表操作,不支持join

这里以一个销售记录表为例:

比如我们有一张销售记录明细表,存储了每个交易的时间,销售员,销售门店,和金额。

提交完创建物化视图的任务后,Doris就会异步在后台生成物化视图的数据,构建物化视图。

在构建期间,用户依然可以正常的查询和导入新的数据。创建任务会自动处理当前的存量数据和所有新到达的增量数据,从而保持和base表的数据一致性。用户不需关心一致性问题。

3.2 物化视图查询

image-20210807093117688

物化视图创建完成后,用户的查询会根据规则自动匹配到最优的物化视图。

比如我们有一张销售记录明细表,并且在这个明细表上创建了三张物化视图。一个存储了不同时间不同销售员的售卖量,一个存储了不同时间不同门店的销售量,以及每个销售员的总销售量。

当查询7月19日,各个销售员都买了多少钱的话。就可以匹配mv_1物化视图。直接对mv_1的数据进行查询。

3.3 物化视图查询自动匹配

image-20210807093159834

物化视图的自动匹配分为下面两个步骤:

1.根据查询条件删选出一个最优的物化视图:这一步的输入是所有候选物化视图表的元数据,根据查询的条件从候选集中输出最优的一个物化视图

2.根据选出的物化视图对查询进行改写:这一步是结合上一步选择出的最优物化视图,进行查询的改写,最终达到直接查询物化视图的目的。

3.4 物化视图最优路径选择

image-20210807093314716

这里分为两个步骤:

1.对候选集合进行一个过滤。只要是查询的结果能从物化视图数据计算(取部分行,部分列,或部分行列的聚合)出都可以留在候选集中,过滤完成后候选集合大小>=1。

2.从候选集合中根据聚合程度,索引等条件选出一个最优的也就是查询花费最少物化视图。

这里再举一个相对复杂的例子,来体现这个过程:

image-20210807093420201

首先先说过滤候选集这个过程

候选集过滤目前分为4层,每一层过滤后去除不满足条件的物化视图。

比如查询7月19日,各个销售员都买了多少钱的话:

首先一开始候选集中包括所有的物化视图以及base表共4个。

第一层过滤先判断查询where中的谓词涉及到的数据是否能从物化视图中得到。也就是销售时间列是否在表中存在。由于第三个物化视图中根本不存在销售时间列。所以在这一层过滤中,mv_3就被淘汰了。

第二层是过滤查询的分组列是否为候选集的分组列的子集。也就是销售员id是否为表中分组列的子集。由于第二个物化视图中的分组列并不涉及销售员id。所以在这一层过滤中,mv_2也被淘汰了。

第三层过滤是看查询的聚合列是否为候选集中聚合列的子集。也就是对销售额求和是否能从候选集的表中聚合得出。这里base 表和物化视图表均满足标准。

最后一层是过滤看查询需要的列是否存在于候选集合的列中。由于候选集合中的表均满足标准,所以最终候选集合中的表为 销售明细表,以及mv_1,这两张。

image-20210807093543996

候选集过滤完后输出一个集合,这个集合中的所有表都能满足查询的需求。但每张表的查询效率都不同。

这时候就需要再这个集合根据前缀索引是否能匹配到,以及聚合程度的高低来选出一个最优的物化视图。

例子:

从表结构中可以看出,base表的销售日期列是一个非排序列,而物化视图表的日期是一个排序列,同时聚合程度上mv_1表明显比base表高。

所以最后选择出mv_1作为该查询的最优匹配。

image-20210807093630376

最后再根据选择出的最优解,改写查询。

例如:刚才的查询选中mv_1后,将查询改写为从mv_1中读取数据,过滤出日志为7月19日的mv_1中的数据然后返回即可。

3.5 物化视图查询改写

image-20210807093727835

有些情况下的查询改写还会涉及到查询中的聚合函数的改写。

比如业务方经常会用到count distinct对 PV UV 进行计算。

例如;

广告点击明细记录表中存放哪个用户点击了什么广告,从什么渠道点击的,以及点击的时间。并且在这个base表基础上构建了一个物化视图表,存储了不同广告不同渠道的用户bitmap值。

由于bitmap union这种聚合方式本身会对相同的用户user id进行一个去重聚合。当用户查询广告在web端的uv的时候,就可以匹配到这个物化视图。

匹配到这个物化视图表后就需要对查询进行改写,将之前的对用户id求count(distinct) 改为对物化视图中bitmap union列求count。

所以最后查询取物化视图的第一和第三行求bitmap聚合中有几个值。

3.6 物化视图使用及限制image-20210807093846274

目前支持的聚合函数包括,常用的sum,min,max count

以及计算pv ,uv, 留存率,等常用的去重算法 hll_union,和用于精确去重计算count(distinct)的算法 bitmap_union。

使用物化视图功能后,由于物化视图实际上是损失了部分维度数据的。所以对表的 DML 类型操作会有一些限制。

比如如果表的物化视图key中不包含删除语句中的条件列,则删除语句不能执行。

比如想要删除渠道为app端的数据,由于存在一个物化视图并不包含渠道这个字段,则这个删除不能执行,因为删除在物化视图中无法被执行。

这时候你只能把物化视图先删除,然后删除完数据后,重新构建一个新的物化视图。

4.物化视图适用场景

image-20210807094003311

上面介绍明细模型和聚合模型,也介绍了两种预聚合方式物化视图。

在数据模型的选择上:

如果用户的分析都是固定维度的分析类查询,比如报表类业务,且完全不关心明细数据时,则用聚合模型最合适。

如果用户需要查询明细数据,比如交易明细,则用明细模型合适。

而对于物化视图和Rollup来说,他们的共同点都是通过预聚合的方式来提升查询效率。

实际上物化视图是Rollup的一个升级,在覆盖Rollup的工作同时,还支持更灵活的聚合方式。

如果对数据的分析需求,既覆盖了明细查询也存在分析类查询,则创建一个明细模型的表,并在上面构建合适的物化视图最适合。

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦