导读:DataTester 是由火山引擎推出的 A / B 测试平台,笼罩举荐、广告、搜寻、UI、产品性能等业务利用场景,提供从 A / B 实验设计、试验创立、指标计算、统计分析到最终评估上线等贯通整个 A / B 试验生命周期的服务。
DataTester 通过了字节跳动业务的多年打磨,在字节外部已累计实现 150 万次 A / B 试验,在内部也利用到了多个行业畛域。指标查问的产品高性能是 DataTester 的一大劣势。作为产品最简单的功能模块之一,DataTester 的指标查问可能在无限资源的前提下,施展出最极致的 A / B 试验数据查问体验,而在这背地是屡次的技术计划的打磨与迭代。
本文将分享 DataTester 在查问性能晋升过程中的 5 个优化思路。
01 现状及问题
- 挑战 1:版本治理试验指标报告页是 DataTester 零碎最外围的性能之一,报告页的应用体验间接决定了 DataTester 作为数据增长和试验评估引擎在业界的竞争力。
该性能具备以下特点:
① 株连零碎多、链路长:报告页波及到控制台(Console)、科学计算模块、查问引擎、OLAP 存储引擎。整个链路包含了:DSL 到 sql 转化、后端查问后果缓存解决、查问后果的加工计算、前端查问接口的组装和数据渲染。
② 实现简单:试验指标有多种算子,在查问引擎侧中都有一套定制 SQL,通过 DSL 将算子转换成 SQL。这是 DataTester 中最简单的功能模块之一。
02 优化思路
从一条 SQL 说起。
举一个例子,在 DataTester 中一次 AB 测试的查问分三局部逻辑。
① 实时扫描事件表,做过滤
② 依据用户首次进组工夫过滤出用户
③ 做聚合运算须要查问具体的 SQL 代码,也能够点击开展查看详情。
printf("hello world!");SELECT event_date,
count(DISTINCT uc1) AS uv,
sum(value) AS sum_value,
sum(pow(value, 2)) AS sum_value_square
FROM
(SELECT uc1,
event_date,
count(s) AS value
FROM
(SELECT hash_uid AS uc1,
TIME,
server_time,
event,
event_date,
TIME AS s
FROM rangers.tob_apps_all et
WHERE tea_app_id = 249532
AND ((event = 'purchase'))
AND (event_date >= '2021-05-10'
AND event_date <= '2021-05-19'
AND multiIf(server_time < 1609948800, server_time, TIME > 2000000000, toUInt32(TIME / 1000), TIME) >= 1620576000
AND multiIf(server_time < 1609948800, server_time, TIME > 2000000000, toUInt32(TIME / 1000), TIME) <= 1621439999)
AND (event in ('rangers_push_send',
'rangers_push_workflow')
OR ifNull(string_params{'$inactive'},'null')!='true') ) et GLOBAL ANY
INNER JOIN
(SELECT min(multiIf(server_time < 1609948800, server_time, TIME > 2000000000, toUInt32(TIME / 1000), TIME)) AS first_time,
hash_uid AS uc2
FROM rangers.tob_apps_all et
WHERE tea_app_id = 249532
AND arraySetCheck(ab_version, (29282))
AND event_date >= '2021-05-10'
AND event_date <= '2021-05-19'
AND multiIf(server_time < 1609948800, server_time, TIME > 2000000000, toUInt32(TIME / 1000), TIME) >= 1620651351
AND multiIf(server_time < 1609948800, server_time, TIME > 2000000000, toUInt32(TIME / 1000), TIME) <= 1621439999
AND (event in ('rangers_push_send',
'rangers_push_workflow')
OR ifNull(string_params{'$inactive'},'null')!='true')
GROUP BY uc2) tab ON et.uc1=tab.uc2
WHERE multiIf(server_time < 1609948800, server_time, TIME > 2000000000, toUInt32(TIME / 1000), TIME)>=first_time
AND first_time>0
GROUP BY uc1,
event_date)
GROUP BY event_date
DataTester 底层 OLAP 引擎采纳的是 clickhouse,依据 clickhouse 引擎的特点,次要有两个优化方向:
① 缩小 clickhouse 的 join,因为 clickhouse 最善于的是单表查问和多维度剖析,如果做一些轻量级聚合把后果做到单表上,性能能够极大晋升。也就是把 join 提前到数据构建阶段,构建好的数据就是 join 好的数据。
② 须要 join 的场景,则通过减小右表大小来减速查问。因为 join 的时候会把右表拉到本地构建 hash 表,所以必然会占用大量内存,影响性能。
- 重点优化计划
- 计划一:预聚合,压缩查问事件量
尽管指标很灵便,然而大多数场景用户进入报告页只会查看进组信息,试验论断,指标天级统计数据等,很少实时带条件去查问。因而,天级查问是咱们次要应用场景。天级查问能够通过「预计算」减速。为了反对置信度的计算,「预计算」能够从人的粒度着手,即每天保留一条人的聚合后后果,记录下这个人在所有试验下进组之后各指标下的累积值。这样每天数据量与日活量相当,能够大大压缩总体查问量。
(1)计划详情
总体流程图:
分为如下几个关键步骤:Dump、Parse、Build、Query
- Dump
即把事件 dump 到离线存储中。私有化采纳 flume 来实现:
a. 自定义 timestamp interceptor 避免数据漂移
b. 应用 file channel 文件缓冲保证数据不失落 - Parse
从指标 DSL 中解析出聚合字段、聚合类型,事件名、过滤条件指标四因素,再依据这些信息生成 md5 作为 clickhouse 存储的 key。思考到不同指标配置可能会配置雷同的聚合字段、聚合类型,事件名、过滤条件,生成 md5 的目标是保障惟一避免屡次聚合。
聚合类型包含 count,sum,max,min,latest,distinct(暂不反对),任何算子都能够用这几个根底聚合后果计算出来。如 avg 能够通过 sum/count 来计算。
- Build
离线构建最外围的局部在于自定义聚合函数(UDAF),自带的聚合函数无奈满足咱们的要求。 - Query
即数据如何查问,通过对查问引擎减少参数管制是否走预聚合逻辑,同时针对预聚合定制了查问实现。(2)资源应用限度
私有化场景用户机器资源是十分贵重的,夜间也有很多定时工作在执行会争抢资源。为了保障不占用太多资源,提交工作时会对 spark 参数做管制。
以如下参数为基准,对 spark.dynamicAllocation.maxExecutors 进行管制:
driver-memory:4g
executor-memory:2g
executor-cores:2
配置梯度表:
(3)性能晋升体现 4 亿事件量,100w 用户量,查问晋升超过 4 倍。
- 计划二:ab_log,减小 join 时右表的大小(1)背景
(2)计划概览
① 从实时流中过滤出曝光事件,把用户和进组工夫写进实时 clickhouse 表。
② 从 clickhouse 实时表中构建出天粒度的离线用户进组信息表,每天每个用户仅有 1 条进组记录,记录了该用户该天最早的进组工夫。
③ 查问的时候,为了取得用户首次进组工夫,取 min(「实时表中该用户当天的进组工夫」,「离线表试验开始到 T - 1 天数据中该用户进组工夫」)。
(3)晋升成果
① 通过天级进组表大大减速服务端试验进组人群的圈选。
② 彻底解决私有化进组用户属性的隐患。
③ 在私有化环境能够肯定水平上缩小曝光事件量。在某些客户下,可缩小 30% 以上事件量。
- 计划三:GroupBy 查问优化
(1)背景
DataTester 的数据查问和其余数据利用产品不同,DataTester 在数据查问时,所有的查问都会针对每一个试验版本都查一遍,而过程中中惟一的区别就在于试验版本 ID,所以和 SQL 中 GroupBy 的利用场景特地符合,通过 GroupBy 查问不仅能够极大的缩小查问的数量,也能够升高屡次查问造成的反复扫表,进步查问效率。
(2)优化计划
DataTester 对每个试验版本的查问语句都是相似的,只是版本 id 不同。对 DataTester 用到的所有查问类型和算子做 GroupBy 的革新,实现细节这里不做过多开展。
(3)晋升成果
测试数据规模为日均一亿,7 天,3 个试验版本
查问引擎接口响应时长(取 10 次均匀):5. 计划四:au 类指标优化,缩小反复查问次数
(1)背景指标查问引擎对 DataTester 的 au 类型算子都做了定制,一个指标查问会产生两条 sql,一条失常指标的查问 sql,另一条是对 any_event 的 au 的查问,在最初后果解决的时候对两条 sql 的查问后果做了一个合并,一起返回到 DataTester 的科学计算模块。然而,每次关上报告页都必定会查进组人数,它和 any_event 的 au 是同一个值,au 类型算子查问的时候无奈复用进组人数的后果,而 au 查问又能够算是最慢的查问之一,升高了报告页关上的速度。
对有进组指标的算子做了缓存优化,缩小反复查问。
(2)优化计划
- 计划五:异步查问优化,解决页面超时问题
(1)背景
DataTester 报告页等一些查问数据的接口自身的确比拟耗时,须要实时计算,而大部分网关都有超时限度,这个问题在私有化中尤为显著,所以对报告页的整体交互做了优化革新。
(2)计划介绍 - 前后端交互
- 服务端架构设计
(3)用户体验改良成果
① 大幅缩短申请延时,避免出现页面申请失败的状况
② 通过减少 redis 缓存,同页面的屡次刷新响应工夫能够管制在 100ms 左右 - 其余优化计划
① 业务逻辑优化,报告概览外围指标显著性和进组共用查问后果,去除试验版本依照外围指标显著性的排序,14 个 SQL 降至 10 个,升高 28.5%⬇️
② 多维度并发管制,限度资源应用
③ 默认应用备查问,充分利用备节点的算力
④ 灵便开关多种报告的缓存,保障外围链路失常运行
03 总结
作为一站式 A / B 测试平台,火山引擎 DataTester 最外围的性能之一就是指标查问局部,它关系到产品体验和资源占用状况。而作为 TOB 畛域的数据产品,DataTester 能在无限的资源下施展最极致的产品数据体验,也是产品最为重要的竞争力之一。
本次分享了 DataTester 在报告页查问优化过程中的 5 个技术计划落地。预聚合和 ablog 是从数据构建角度缩小查问数据量的角度的优化,groupby 和 au 类指标的优化是从缩小并发的角度,异步查问是从产品体验角度。
查问和数据构建密不可分,DataTester 将来的产品优化也会依照“去肥”和“增瘦”两个方向进行,“去肥”是优化科学计算模块和查问引擎的整体架构,优化业务逻辑,使得报告页查问逻辑更加清晰和简洁;另一方面“增瘦”就是通过正当的数据构建和数据模型优化减速查问,同时定向对局部难点问题重点优化,比方留存、盒须快照、同期群等等。
点击跳转火山引擎 A / B 测试 DataTester 官网理解详情!
扫码进入「字节跳动数据平台」官网交换群,支付更多 A / B 测试学习材料。