写点什么

【遇见 Doris】Doris 核心功能介绍——数据模型和物化视图

用户头像
ApacheDoris
关注
发布于: 2021 年 03 月 24 日

感谢 7 月 25 日来参加 Doris 与 Dolphin Scheduler 合作举办的线上 Meetup 的小伙伴们,现在为大家带来 Meetup 的内容。

本次 Meetup 请到了来自百度、奇安信、美团点评和易观的技术大牛带来技术分享。了解更多详情请关注 Doris 官方公众号。嘉宾分享回顾会陆续放出,公众号后台回复“0725”立即 get 回放录像及嘉宾 PPT。


本次为大家带来的是第一期内容回顾: 《Doris 核心功能介绍——数据模型和物化视图》


本期主讲人:

缪翎

百度研发工程师

Doris PPMC


Doris 是什么


Doris 的两种数据模型


Doris 的物化视图


适用场景总结 


Doris 是什么


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

应用场景包括:固定历史报表分析、实时数据分析、交互式数据分析等。


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



目前使用 Doris 的公司包括:京东,美团,小米等

Doris 主要覆盖的业务场景有: 用户行为分析,广告点展销等

 

Doris 的两种数据模型


1. 聚合模型 


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

 

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


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


首先是导入数据 ,原始数据在导入过程中,会根据表结构中的 Key 进行分组,相同 Key 的 Value 会根据表中定义的 Aggregation Function 进行聚合。



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


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


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



Compaction 的意思其实就是将不同版本的数据进行合并。它 分为两个阶段,第一个阶段是: 当 Singleton 的数据版本个数到达 Doris 设置的阈值时,就会触发 Cumulative 级别的 Compaction。 这个级别的 Compaction 会将一个区间段内的版本数据根据定义好的聚合函数进行再聚合。 


例如: C umulatives 会将 61~70 的这 10 个 Singleton 版本的数据合并成一个版本。这个版本的范围就是 61~70。


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


例如: 上图中的 Base Compaction 已经完成后第 0 个版本到第 60 个版本的聚合。那么下一次的 Base Compaction 就是把 61~70 这个已经完成 Cumulative 合并的版本和 0~60 版本再进行一个 Base Compaction,最终生成一个 0~70 的版本。


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



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


这时候版本 0~30 中订单 1 的状态可能是待付款,版本 31~35 中订单 1 的状态是已收货,最后一个版本 36 中订单 1 的状态是已完成。 那么用户在查询每个订单状态的时候,不同版本之间的数据就需要进行一个 Replace, 取最后一个版本中的已完成状态作为查询的结果。


说完聚合模型,再介绍一种聚合模型上的 提升查询效率 的方式—— 构建 Rollup

 


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


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


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


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



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


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


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


例如: 12 月 31 日广告 1 在美国的点击量增加一次,则 Base 表中的点击量就会变成 11,而 Rollup 表中的点击量就会变成 41。


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


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


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


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

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

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

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


基于以上问题,我们增加了对明细数据模型的支持。


2. 明细模型



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


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


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

   

Doris 的物化视图

 

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


首先,什么是物化视图?


 

从定义上来说,就是包含了查询结果的数据库对象,可能是对远程数据的本地 Copy;也可能是一个表或多表 Join 后结果的行或列的子集;也可能是聚合后的结果。说白了,就是预先存储查询结果的一种数据库对象。


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


它的优势在于:

  1. 对于那些经常重复的使用相同的子查询结果的查询性能大幅提升

  2. Doris 自动更新物化视图的数据,保证 Base 表和物化视图表的数据一致性。无需额外的维护成本

  3. 查询的时候也可以自动匹配最优的物化视图


1. 创建


下面就来看一下物化视图是怎么使用的。



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


创建物化视图表的语法和 PostgreSQL、Oracle 都是一致的。


这里以一个销售记录表为例:比如我们有一张销售记录明细表,存储了每个销售记录的 id,销售员,售卖时间,和金额。 提交完创建物化视图的任务后,Doris 就会异步在后台生成物化视图的数据,构建物化视图。在构建期间,用户依然可以正常的查询和导入新的数据。创建任务会自动处理当前的存量数据和所有新到达的增量数据,从而保持和 Base 表的数据一致性。 用户无需担心一致性问题 。

 

2. 查询


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



例如: 我们有一张销售记录明细表,并且在这个明细表上创建了三张物化视图。一个存储了不同时间不同销售员的售卖量,一个存储了不同时间不同门店的销售量,以及每个销售员的总销售量。 当查询 7 月 19 日各个销售员都买了多少钱时,我们 可以匹配 mv_1 物化视图, 直接对 mv_1 的数据进行查询。

 

3. 自动匹配



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

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

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


4. 选择最优


下面详细解释一下第一步最优物化视图是被如何选择出来的。 



这里分为两个步骤:

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

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


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

 


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


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


例如: 查询 7 月 19 日各个销售员都买了多少钱。 首先一开始候选集中包括所有的物化视图以及 Base 表共 4 个。第一层 过滤先判断查询 Where 中的谓词涉及到的数据是否能从物化视图中得到,也就是销售时间列是否在表中存在。由于第三个物化视图中根本不存在销售时间列。所以在这一层过滤中,mv_3 就被淘汰了。 第二层 是过滤查询的分组列是否为候选集的分组列的子集,也就是 销售员 id 是否为表中分组列的子集。由于第二个物化视图中的分组列并不涉及 销售员 id 。所以在这一层过滤中,mv_2 也被淘汰了。 第三层 过滤是看查询的聚合列是否为候选集中聚合列的子集,也就是对销售额求和是否能从候选集的表中聚合得出。这里 Base 表和物化视图表均满足标准。 最后一层 是过滤看查询需要的列是否存在于候选集合的列中。由于候选集合中的表均满足标准,所以最终候选集合中的表为 销售明细表 ,以及 mv_1 这两张。

 


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


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


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


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

 

5. 查询改写


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



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

 

有些情况下的查询改写还会涉及到查询中的聚合函数的改写。 比如业务方经常会用到 Count、Distinct 对 PV、UV 进行计算。



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


由于 bitmap_union 这种聚合方式本身会对相同的用户 user_id 进行一个去重聚合。当用户查询广告在 Web 端的 UV 的时候,就可以匹配到这个物化视图。 匹配到这个物化视图表后就需要对查询进行改写,将之前的对用户 id 求 count(distinct) 改为对物化视图中 bitmap_union 列求 count。


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

 

6. 物化视图


目前支持的聚合函数包括常用的 sum、min、max、count 以及 pv、uv, 留存率等计算时常用的去重算法 hll_union,和用于精确去重计算 count(distinct) 的算法 bitmap_union。



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


例如: 如果表的物化视图 Key 中不包含删除语句中的条件列,则删除语句不能执行。如果 想要删除渠道为 APP 端的数据,由于存在一个物化视图并不包含渠道这个字段,则这个删除不能执行,因为删除在物化视图中无法被执行。 这时候你只能把物化视图先删除,然后删除完数据后,重新构建一个新的物化视图。

 

未来还会支持构建年表、月表,会用的到 to_mouth 和 to_day 函数。


适用场景

 

上文介绍了两种数据模型——明细模型和聚合模型,也介绍了两种预聚合方式物化视图和 Rollup。那么 在数据模型的选择上:如果用户的分析都是 固定维度的分析类查询 ,比如报表类业务,且完全不关心明细数据时,则用聚合模型最合适。如果用户 需要查询明细数据 ,比如交易明细,则用明细模型合适。


而对于物化视图和 Rollup 来说,他们的共同点都是 通过预聚合 的方式来提升查询效率。 实际上物化视图是 Rollup 的一个超集,在覆盖 Rollup 的工作同时,还支持更灵活的聚合方式。


因此,如果对数据的分析需求既 覆盖了明细查询也存在分析类查询 ,则可以先创建一个明细模型的表,并构建物化视图。


欢迎扫码关注:



Apache Doris(incubating)官方公众号


相关链接:


Apache Doris 官方网站:


http://doris.incubator.apache.org


Apache Doris Github:

https://github.com/apache/incubator-doris

Apache Doris 开发者邮件组:

dev@doris.apache.org 


发布于: 2021 年 03 月 24 日阅读数: 18
用户头像

ApacheDoris

关注

还未添加个人签名 2021.03.17 加入

Doris(原百度Palo https://cloud.baidu.com/product/palo.html )是一款基于大规模并行处理技术的分布式 SQL 数据仓库,由百度在2017年开源,2018年进入 Apache 孵化器

评论

发布
暂无评论
【遇见Doris】Doris核心功能介绍——数据模型和物化视图