关于数据库:小米-AB-实验场景基于-Apache-Doris-的查询提速优化实践

3次阅读

共计 7009 个字符,预计需要花费 18 分钟才能阅读完成。

导读: Apache Doris 是小米团体外部利用最为宽泛的 OLAP 引擎之一,本文次要从数据的角度剖析 A/B 试验场景查问的性能现状,探讨基于 Apache Doris 的性能优化的解决方案。通过一系列基于 Doris 的性能优化和测试,A/B 试验场景查问性能的晋升超过了咱们的预期。心愿本次分享能够给有须要的敌人提供一些参考。

作者|小米团体大数据工程师 乐涛

业务背景

A/B 试验是互联网场景中比照策略优劣的重要伎俩。为了验证一个新策略的成果,须要筹备原策略 A 和新策略 B 两种计划。随后在总体用户中取出一小部分,将这部分用户齐全随机地分在两个组中,使两组用户在统计角度无差别。将原策略 A 和新策略 B 别离展现给不同的用户组,一段时间后,联合统计办法剖析数据,失去两种策略失效后指标的变动后果,并以此判断新策略 B 是否合乎预期。

小米 A/B 试验平台是一款通过 A/B 试验的形式,借助试验分组、流量拆分与迷信评估来辅助实现迷信的业务决策,最终实现业务增长的一款经营工具产品。其宽泛的利用于产品研发生命周期中各个环节:

数据平台架构

本文次要从数据的角度剖析 A/B 试验场景查问的性能现状,探讨一下基于 Apache Doris 的性能优化的解决方案。A/ B 试验平台的架构如下图所示:

  • 平台应用的数据次要蕴含平台自用的试验配置数据、元数据,以及业务方上报的日志数据。
  • 因为业务方引入 SDK,并与分流服务进行交互,日志数据中蕴含其参加的实验组 ID 信息。
  • 用户在试验平台上配置、剖析、查问,以取得报告论断满足业务诉求。

鉴于 AB 实验报告各个业务方上报数据的链路都大体相似,咱们就拿 头部业务方广告业务 举例,数据流程如下图所示:

从上图可知,整个数据链路并不简单,日志数据传入后,通过必要的数据处理和荡涤工作进入 Talos(小米自研音讯队列),通过 Flink 工作以明细数据的模式实时写入到 Doris 表中,同时 Talos 数据也会同步到 Hive 表进行备份,以便问题排查和数据修复。

出于对高效写入以及字段增减需要的思考,Doris 明细表以 Duplicate 模型来建模

CREATE TABLE `dwd_xxxxxx` (`olap_date` int(11) NULL COMMENT "分区日期",
  `user_id` varchar(256) NULL COMMENT "用户 id",
  `exp_id` varchar(512) NULL COMMENT "实验组 ID",
  `dimension1` varchar(256) NULL COMMENT "",
  `dimension2` varchar(256) NULL COMMENT "",
  ......
  `dimensionN` bigint(20) NULL COMMENT "",
  `index1` decimal(20, 3) NULL COMMENT "",
  ......
  `indexN` int(11) NULL COMMENT "",
​
) ENGINE=OLAP
DUPLICATE KEY(`olap_date`, `user_id`)
COMMENT "OLAP"
PARTITION BY RANGE(`olap_date`)
(PARTITION p20221101 VALUES [("20221101"), ("20221102")),
PARTITION p20221102 VALUES [("20221102"), ("20221103")),
PARTITION p20221103 VALUES [("20221103"), ("20221104"))
)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 300
;
数据现状剖析

在提速之前,小米 A/B 试验平台实现实验报告查问的 P95 工夫为 小时级,实验报告应用数据的形式存在诸多的性能问题,间接影响业务部门做经营和决策的效率。

报告查问基于明细

以后报告查问的数据起源为明细表,而明细表的数据量微小:

而且,实验报告的查问条件中工夫范畴经常横跨多天。基于历史查问报告统计,查问条件中工夫范畴大于一天的报告占比 69.1%,具体的时间跨度占比散布如下:

明细数据的微小扫描量给集群带来了不小的压力,且因为报告查问存在并发以及 SQL 的拆分,如果一个 SQL 申请不能疾速的返回后果开释资源,也会影响到申请的排队情况。因而在工作时间段内 Doris 集群 BE 节点 CPU 负载情况根本是继续满载,磁盘 IO 也继续处于高负荷状态,如下图所示:

BE 节点 CPU 使用率

BE 节点磁盘 IO

集体思考:

  • 以后报告所有查问基于明细数据,且均匀查问时间跨度为 4 天,查问扫描数据量上百亿。因为扫描数据量级大,计算成本高,给集群造成较大压力,导致数据查问效率不高。
  • 如果通过对数据进行预聚合解决,管制 Scan Rows 和 Scan Bytes,减小集群的压力,查问性能会大幅晋升。

字段查问热度分层散布

因为之前流程管控机制绝对宽松,用户增加的埋点字段都会进入到明细表中,导致字段冗余较多。统计历史查问报告发现,明细表中罕用的维度和指标只集中在局部字段,且查问热度分层散布:

参加计算的指标也集中在局部字段,且大部分都是聚合计算 (sum) 或能够转化为聚合计算(avg):

集体思考:

  • 明细表中参加应用的维度只占 54.3%,高频应用的维度只占 15.2%,维度查问频次分层散布。
  • 数据聚合须要对明细表中维度字段做取舍,抉择局部维度进行上卷从而达到合并的目标,但舍弃局部字段必然会影响聚合数据对查问申请的笼罩状况。而维度查问频次分层散布的场景非常适合依据维度字段的热度做不同档次的数据聚合,同时兼顾聚合表的聚合水平和覆盖率。

实验组 ID 匹配效率低

以后明细数据的格局为:

明细数据中的实验组 ID 以逗号分隔的字符串模式聚拢在一个字段中,而实验报告的每条查问语句都会应用到 exp_id 过滤,查问数据时应用 LIKE 形式匹配,查问效率低下。

集体思考:

  • 将实验组 ID 建模成一个独自的维度,可应用齐全匹配代替 LIKE 查问,且可利用到 Doris 索引,进步数据查问效率。
  • 将逗号分隔的实验组 ID 间接打平会引起数据量的急剧收缩,因而须要设计正当的计划,同时兼顾到数据量和查问效率。

进组人数计算有待改良

进组人数查问是实验报告的必查指标,因而其查问速度很大水平上影响实验报告的整体查问效率,以后次要问题如下:

  • 当进组人数作为独立指标计算时,应用近似计算函数 APPROX_COUNT_DISTINCT 解决,是通过就义准确性的形式晋升查问效率。
  • 当进组人数作为复合指标的分母进行计算时,应用 COUNT DISTINCT 解决,此形式在大数据量计算场景效率较低。

集体思考:

  • AB 实验报告的数据论断会影响到用户决策,就义准确性的形式晋升查问效率是不可取的,特地是广告这类波及金钱和业绩的业务场合,用户不可能承受近似后果。
  • 进组人数应用的 COUNT DISTINCT 计算须要依赖明细信息,这也是之前查问基于明细数据的重要因素。必须为此类场景设计新的计划,使进组人数的计算在保证数据精确的前提下提高效率。

数据优化计划

基于以上的数据现状,咱们优化的外围点是将明细数据预聚合解决,通过压缩数据来管制 Doris 查问的 Scan Rows 和 Scan Bytes。与此同时,使聚合数据尽可能多的笼罩报告查问。从而达到,减小集群的压力,进步查问效率的目标。

新的数据流程如下图所示:

整个流程在明细链路的根底上减少聚合链路,Talos 数据一方面写入 Doris 明细表,另一方面增量落盘到 Iceberg 表中,Iceberg 表同时用作回溯明细数据以及生成聚合数据,咱们通过工场 Alpha(小米自研数据开发平台)的实时集成和离线集成保障工作的稳固运行和数据的一致性。

选取高频应用维度聚合

在生成数据聚合的过程中,聚合水平与申请覆盖率是负相关的。应用的维度越少,能笼罩的申请就越少,但数据聚合水平越高;应用的维度越多,笼罩的申请也越多,但数据粒度就越细,聚合水平也越低。因而须要在聚合表建模的过程中获得一个均衡。

咱们的具体做法是:拉取历史(近半年)查问日志进行剖析,依据维度字段的应用频次排序确认进入聚合表的优先级。在此基础上得出聚合表的覆盖率和数据量随着建模字段减少而变动的曲线,如下图所示:

其中覆盖率依据历史申请日志代入聚合表计算得出。

咱们的准则是:针对 OLAP 查问,聚合表的数据量应尽可能的管制在单日 1 亿条 以内,申请覆盖率尽可能达到 80% 以上

因而不难得出结论:抉择 14 个维度字段对聚合表建模比拟现实,数据量能管制到单日 8 千万 条左右,且申请覆盖率约为 83%

应用物化视图

在剖析报告历史查问日志时,咱们发现不同的维度字段查问频次有显著的分层:

Top7 维度字段简直呈现在所有报告的查问条件之中,对于如此高频的查问,值得做进一步的投入,使查问效率尽可能的晋升到最佳。Doris 的物化视图可能很好的服务于此类场景。

什么是物化视图?

物化视图是一种非凡的 物理表 ,其中保留基于基表(base table) 局部字段进一步 上卷聚合 的后果。

尽管在物理上独立存储,但它是对 用户通明 的。为一张基表配置好物化视图之后,不须要为其写入和查问做任何额定的工作:

  • 当向基表写入和更新数据时,集群会主动同步到物化视图,并通过事务形式保证数据一致性。
  • 当对基表进行查问时,集群会主动判断是否路由到物化视图获取后果。当查问字段能被物化视图齐全笼罩时,会优先应用物化视图。

因而咱们的查问路由如下图所示:

用户的查问申请会尽可能的路由到聚合表物化视图,而后是聚合表基表,最初才是明细表。

如此应用 多梯度 的聚合模型的配合来应答 热度分层 的查问申请,使聚合数据的效力尽可能的施展到最大。

准确匹配取代 LIKE 查问

既然物化视图这么好用,为什么咱们不是基于 Doris 明细表配置物化视图,而是独自开发聚合表呢?是因为明细数据中的实验组 ID 字段存储和查问形式并不合理,聚合数据并不适宜通过明细数据间接上卷来失去。

上文中曾经提到,exp_id(实验组 ID)在明细表中以逗号分隔的字符串进行存储,查问数据时应用 LIKE 形式匹配。作为 AB 实验报告查问的 必查条件 ,这种查问形式无疑是 低效 的。

咱们心愿的聚合形式如下图所示:

咱们须要将 exp_id 字段拆开,把数据打平,应用准确匹配来取代 LIKE 查问,进步查问的效率。

管制聚合表数据量

如果只做拆分打平的解决必然会导致数据量的激增,未必能达到正向优化的成果,因而咱们还须要想方法来压缩 exp_id 打平后的数据量:

  • 聚合表选取维度字段建模的时候,除了上文提到的,以字段的应用频次热度作为根据之外,也要关注字段的 取值基数 ,进行综合取舍。如果取值基数过高的维度字段进入聚合表,必然会对管制聚合表的数据量造成妨碍。因而,咱们在保障聚合表申请笼罩量的前提下,酌情舍弃局部 高基数(取值有十万种以上)的维度。
  • 从业务的角度尽可能过滤有效数据(比方一个实验组的流量为 0% 或者 100%,业务上就没有对照的意义,用户也不会去查,这样的数据就不须要进入聚合表)。

通过这一系列步骤,最终聚合表的数据量被管制在单日约 8000 万条,并没有因为 exp_id打平而收缩。

值得一提的是,exp_id字段拆分后,除了查问从 LIKE 匹配变为准确匹配,还额定带来了两项收益:

  • 字段从 String 类型变为 Int 类型,作为查问条件时的比对效率变高。
  • 能利用 Doris 的 前缀索引 布隆过滤器 等能力,进一步提高查问效率。

应用 BITMAP 去重代替 COUNT DISTINCT

要提速实验报告查问,针对进组人数(去重用户数)的优化是十分重要的一个局部。作为一个对明细数据强依赖的指标,咱们如何在不失落明细信息的前提下,实现像 Sum,Min,Max 等指标一样高效的预聚合计算呢?BITMAP 去重计算能够很好的满足咱们的需要。

什么是 BITMAP 去重?

BITMAP 去重简略来说就是建设一种数据结构,表现形式为内存中间断的二进制位(bit),参加去重计算的每个元素(必须为整型)都能够映射成这个数据结构的一个 bit 位的下标,如下图所示:

计算去重用户数时,数据以 bit_or的形式进行合并,以 bit_count 的形式失去后果。更重要的是,如此能实现去重用户数的预聚合。BITMAP 性能劣势次要体现在两个方面:

  • 空间紧凑:通过一个 bit 位是否置位示意一个数字是否存在,能节俭大量空间。以 Int32 为例,传统的存储空间为 4 个字节,而在 BITMAP 计算时只需为其调配 1/8 字节(1 个 bit 位)的空间。
  • 计算高效:BITMAP 去重计算包含对给定下标的 bit 置位,统计 BITMAP 的置位个数,别离为 O(1) 和 O(n) 的操作,并且后者可应用 CLZ,CTZ 等指令高效计算。此外,BITMAP 去重在 Doris 等 MPP 执行引擎中还能够并行减速解决,每个节点各自计算本地子 BITMAP,而后进行合并。

当然,以上只是一个简化的介绍,这项技术倒退至今曾经做了很多优化实现,比方 RoaringBitmap,感兴趣的同学能够看看:https://github.com/RoaringBit…

全局字典

要实现 BITMAP 去重计算,必须保障参加计算的元素为 UInt32 / UInt64,而咱们的 user_idString类型,因而咱们还需设计保护一个全局字典,将 user_id 映射为数字,从而实现 BITMAP 去重计算。

因为聚合数据目前只服务于离线查问,咱们抉择基于 Hive 表实现全局字典,其流程如下:

指标聚合

生成 Doris 聚合表时,将 user_id作为查问指标以 BITMAP 类型来存储,其余惯例查问指标则通过 COUNT/SUM/MAX/MIN 等形式聚合:

如此明细表和聚合表的指标计算对应关系如下:

优化成果

SQL 视角

查问申请转换成 SQL 之后,在明细表和聚合表的体现比照如下:

  • 惯例聚合指标查问的性能晋升自不必说(速度晋升 50~60 倍)
  • 进组人数查问性能的晋升也十分可观(速度晋升 10 倍左右)

集群视角

SQL 查问的快进快出,使查问占用的资源能疾速开释,对集群压力的缓解也有正向的作用。

Doris 集群 BE 节点 CPU 应用状况和磁盘 IO 情况的扭转效果显著:

须要阐明的是,集群情况的改善(包含实验报告查问 P95 晋升)并不全归功于数据预聚合优化工作,这是各方合力合作(如产品业务状态调整,后端查问引擎排队优化,缓存调优,Doris 集群调优等)的综合后果。

小技巧

因为业务查问需要的多样,在查问明细表时,会呈现一个字段 既作为维度又作为指标 来应用的状况。

如广告业务表中的 targetConvNum(指标转化个数) 字段,此字段的取值为 0 和 1,查问场景如下:

-- 作为维度
select targetConvNum,count(distinct user_id)
from analysis.doris_xxx_event 
where olap_date = 20221105
and event_name='CONVERSION'
and exp_id like '%154556%'
group by targetConvNum;
​
-- 作为指标
select sum(targetConvNum)
from analysis.doris_xxx_event 
where olap_date = 20221105
and event_name='CONVERSION'
and exp_id like '%154556%';

如果这个字段被选取进入聚合表,应该如何解决呢?

咱们的解决形式是:

  • 在聚合表中把这类字段建模成 维度
  • 聚合表中须要一个计数指标 cnt,示意聚合表中一条数据由明细表多少条数据聚合得
  • 当这类字段被作为指标查问时,可将其与 cnt 指标配合计算失去正确后果

明细表查问:

select sum(targetConvNum)
from analysis.doris_xxx_event 
where olap_date = 20221105
and event_name='CONVERSION'
and exp_id like '%154556%';
对应的聚合表查问:select sum(targetConvNum * cnt)
from agg.doris_xxx_event_agg
where olap_date = 20221105
and event_name = 'CONVERSION' 
and exp_id = 154556;

结束语

通过这一系列基于 Doris 的性能优化和测试,A/B 试验场景查问性能的晋升超过了咱们的预期。值得一提的是,Doris 较高的稳定性和齐备的监控、剖析工具也为咱们的优化工作提效不少。 心愿本次分享能够给有须要的敌人提供一些参考。

最初,感激 SelectDB 公司和 Apache Doris 社区对咱们的鼎力支持。Apache Doris 是小米团体外部利用最为宽泛的 OLAP 引擎之一,目前团体外部正在推动最新的向量化版本升级工作。将来一段时间咱们将会把业务优化工作和 Doris 最新的向量化版本进行适配,进一步助力业务的正向倒退。

End

最初,欢送更多的开源技术爱好者退出 Apache Doris 社区,携手成长,共建社区生态。Apache Doris 社区以后已包容了上万名开发者和使用者,承载了 30+ 交换社群,如果你也是 Apache Doris 的爱好者,扫码退出 Apache Doris 社区用户交换群,在这里你能够取得:

  • 业余全职团队技术支持
  • 间接和社区专家交换,获取收费且业余回复
  • 意识不同行业的开发者,播种常识以及单干机会
  • Apache Doris 最新版本优先体验权
  • 获取一手干货和资讯以及流动优先参与权

正文完
 0