编者按:Apache Doris 是华为集团公司外部应用领域最广为的 OLAP 发动机之一,责任编辑主要就从统计数据数据的视角预测 A/B 试验情景查阅的操控性现况,深入探讨如前所述 Apache Doris 的操控性强化的软件系统。经过一连串如前所述 Doris 的操控性强化和试验,A/B 试验情景查阅操控性的提高超过了我们的市场预期。期望此次撷取可以给有须要的好友提供一些参照。
译者|华为集团公司大统计数据数据技师 乐涛
销售业务大背景
A/B 试验是网络情景中对照思路好坏的重要方式。为了校正一个卵蛤属的效用,须要准备原思路 A 和卵蛤属 B 三种计划。随即在整体使用者中抽出少部分,将该些使用者完全乱数王骏勇在两个第四组,使三组使用者在统计数据视角LX1。将原思路 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=OLAPDUPLICATE 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/RoaringBitmap/RoaringBitmap
全局字典
要实现 BITMAP 去重计算,必须保证参与计算的元素为 UInt32 / UInt64,而我们的user_id为String类型,因此我们还需设计维护一个全局字典,将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 = 20221105and event_name=CONVERSIONand exp_id like %154556%group by targetConvNum;–作为分项select sum(targetConvNum)from analysis.doris_xxx_event where olap_date = 20221105and event_name=CONVERSIONand exp_id like %154556%;
如果这个字段被选取进入聚合表,应该如何处理呢?
我们的处理方式是:
在聚合表中把这类字段建模成维度
聚合表中须要一个计数分项 cnt,表示聚合表中一条统计数据数据由明细表多少条统计数据数据聚合得
当这类字段被作为分项查阅时,可将其与cnt分项配合计算得到正确结果
明细表查阅:
select sum(targetConvNum)from analysis.doris_xxx_event where olap_date = 20221105and event_name=CONVERSIONand exp_id like %154556%;
对应的聚合表查阅:
select sum(targetConvNum * cnt)from agg.doris_xxx_event_aggwhere olap_date = 20221105and event_name = CONVERSION and exp_id = 154556;
结束语
经过这一连串如前所述 Doris 的操控性强化和试验,A/B 试验情景查阅操控性的提高超过了我们的市场预期。值得一提的是,Doris 较高的稳定性和完备的监控、预测辅助工具也为我们的强化工作提效不少。期望此次撷取可以给有须要的好友提供一些参照。
最后,感谢 SelectDB 公司和 Apache Doris 社区对我们的鼎力支持。Apache Doris 是华为集团公司外部应用领域最广为的 OLAP 发动机之一,目前集团公司外部正在推进最新的向量化版本升级工作。未来一两年我们将会把销售业务强化工作和 Doris 最新的向量化版本进行适配,进一步助力销售业务的正向发展。