共计 3277 个字符,预计需要花费 9 分钟才能阅读完成。
更多技术交换、求职机会,欢送关注字节跳动数据平台微信公众号,回复【1】进入官网交换群
因为流量红利逐步消退,越来越多的广告企业和从业者开始摸索精细化营销的新门路,取代以往的全流量、粗放式的广告轰炸。精细化营销意味着要在数以亿计的人群中优选出那些最具后劲的指标受众,这无疑对提供根底引擎反对的数据仓库能力,提出了极大的技术挑战。
本篇内容将聚焦字节跳动 OLAP 引擎技术和落地教训,从广告营销场景登程,上篇解说利用 ByteHouse 减速实时人群包剖析查问的技术原理;下篇以字节跳动外部场景为例,具体拆解广告业务的实现逻辑和业务成果。(文本为下篇)
广告精准投放场景
广告投放过程个别蕴含数据收集 -> 数据整合 -> 人群圈选 -> 广告投放 -> 反馈剖析等要害流程,人群圈选是广告精准投放的关键步骤,它帮忙确定广告指标受众,辅助投放平台依据不同受众和广告指标优化投放策略,晋升广告收益;
人群预估
人群预估次要是依据肯定的圈选条件,确认命中的用户数目。在广告精准投放过程中,广告主须要晓得以后选定的人群组合中大略会有多少人,用于辅助判断投放状况进而确定投放估算,通常要求计算工夫不能超过 5 秒。
广告投放
广告精准投放过程中遇到的问题与痛点:
数据预估:广告主须要对选定的人群组合进行预估,以便判断投放状况并确定投放估算。但人群包数据量多,基数大。平台的用户数上亿,仅抖音的 DAU 就几亿,抖音、头条对应的人群包在亿级别,晚期的预估版本采纳 ElasticSearch,但因为数据过于宏大,只能采纳 1 /10 抽样存储,导致 10% 的误差,业务难以承受。
查问性能:广告主能够设定一个非常复杂的圈选条件,导致计算简单(单次计算可能蕴含几百上千个人群包),Hive 和 ES 等计划在解决大数据量时,查问速度会变得十分慢,如果须要查问某个广告主的所有用户,须要扫描整个用户库,而这个过程可能须要几分钟甚至几个小时,无奈满足实时性要求。
存储空间大:Hive 和 ES 等计划须要额定的索引构造,导致存储空间变大,从而减少了存储老本。例如,如果须要对用户属性进行索引,就须要额定的存储空间来存储索引数据。
不反对高并发:Hive 和 ES 等计划在解决高并发申请时,容易呈现性能问题,无奈反对高效的广告投放。例如,如果同时有多个广告主须要查问用户信息,就可能会呈现查问阻塞或响应提早等问题。
数据查问效率:采纳 ClickHouse 反对预估,但随着数据量的增长,ClickHouse 在以后存储引擎的反对下也难以保障查问工夫。这导致了数据查问效率的问题,影响了用户体验。
ByteHouse BitEngine 计划
计划简介
新查问引擎
针对广告人群预估业务开发的新查问引擎,基于 ClickHouse 提供的 MergeTree Family 系列引擎,增加了新的 bitmap64 类型和一系列的相干聚合函数。BitEngine 提供的 bitmap64 类型适宜存储和计算大量的用户 ID 之间的关系;在广告人群预估业务中,bitmap64 类型用于存储人群包数据,而后将人群包之间的交并补计算转化为 bitmap 之间的交并补,从而达到远超一般查问的性能指标。
实现步骤
创立一个 bitmap64 类型,能够将用户 ID 间接存储在 bitmap 中,提供一系列交并补的聚合计算,并且还心愿能够充分利用多核 CPU 的并行计算能力,由此咱们设计了 BitEngine。示例如下
CREATE TABLE cdp.tag_uids_map (
tags String,
uids BitMap64 BitEngineEncode
)ENGINE = HaMergeTree('/clickhouse/xxxx/{shard}', '{replica}')
ORDER BY tag
tag_uids_map 存储格局如下
要查问 A&B 的后果 SQL 为
SELECT bitmapCount(‘A&B’) FROM tag_uids_mapBitEngine
实现逻辑
核心思想
- 对数据做分区划分和编码,保障每个区间的数据之间不存在交加,而后应用 roaring bitmap 保留数据;
- 计算时每个分区的数据能够独立的做聚合计算,充分利用机器的并行能力,每个分区外部的聚合计算就是多个 bitmap 之间的交并补,利用 roaring bitmap 高效的交并补计算升高 CPU 和内存的应用;
- 通过字典将编码的后果反解回来,数据编码是为了让数据的散布尽可能浓密,roaring bitmap 在存储和计算的时候就能够取得更好的性能。
业务利用
业务要害因素
- 人群包:广告主自定义规定计算出来的人群数据,标签是 dmp 团队依据市场需求定义的人群数据。
- 标签 ID:每天定时依据产出规定更新一次,人群 ID 是自增的,每天依据广告主需要进行新建计算。
对立编码
- 为了对标签数据和人群数据的 uid 对立编码,编码服务先将标签数据中的 uid 和人群数据中的 uid 提取进去进行对立编码,将全量 uid 平均 hash 到一万个桶中,桶编号为 i[0<=i<=9999],uid 在每个桶内由 1 开始程序编码,每个桶的范畴为 i 2^40 – (i+1)2^40。
- uid 数据每天都在减少,因而须要反对增量编码,编码服务每天会先获取增量 uid,hash 后程序搁置到每个桶中。
数据存储
- 实现编码后,会先把字典数据对立写入 hive 表中,便于字典的各种应用场景。
- 在数据通过分区和编码之后,ClickHouse 能够以多种数据导入格局将数据以 bitmap64 类型存入磁盘。
数据计算
BitEngine 如何充沛利用计算机的并行能力实现每个分区多个 bitmap 之间的交并补计算?
存在问题:
假如存在四个 bitmap,别离为 a,b,c,d;则 (a | c) & (b | d) 不肯定等于(a & b) | (c & d)。
人群包
- 人群包 A = [10001, 20001,30001,40001,50001],人群包 B = [10001, 20001,20002,20003,20004]
冀望后果
通过 BitEngine 计算 A &B = [10001, 20001]
设计方案
- 人群包依照肯定的规定划分为多个区间,任意两个区间之间的人群包没有交加
- 一个计算线程只读取同一个区间的人群包进行计算,失去一个两头后果
- 最终的两头后果只须要简略的进行 bitmap or 计算即可
对于这个设计,BitEngine 须要保证数据的读取和计算是严格依照区间进行。BitEngine 在数据读取时会为每一个文件构建一个读工作,由一个线程调度模块实现整个工作的调度和读取,这个线程调度模块的调度准则是:
- 不同分区的文件不会穿插读取(ClickHouse 的文件读取粒度小于文件粒度,会存在多个线程先后读一个文件的状况,一个分区也可能由多个文件组成),即一个线程只会读 A_1,B_1,不会在这之间读取 A_2 或者 B_2。
- 一个分区读取实现后,能够立刻触发聚合计算,执行 bitmap 之间的计算逻辑,取得两头后果。即 A_1,B_1 读取实现后,能够立刻计算 A_1 & B_1。
- 线程计算完两头后果后,能够持续读其余文件
BitEngine 实现所有两头后果的计算后,会依照后果的输入要求做一次数据合并:
- 如果须要计算的后果是 bitmap 的基数的时候,BitEngine 间接将各个两头后果的基数相加
- 如果计算结果须要的是 bitmap,BitEngine 间接将所有的 bitmap 合并起来,这里合并指的是 bitmap or 计算
业务成果
广告业务成果
- 数据存储空间放大了 3 倍 +
- 导入工夫放大了 3 倍 +
- 查问 avg/pct99/max 都降落显著,pct99 从 5 s 升高到 2 s
- CPU 应用降落显著,PageCache 节俭 100 G+
- 查问误差从 10% 降落到 0%
BitEngine 上线前后查问耗时监控
BitEngine 上线后 CPU 负载比照
PageCache 应用状况(lower is better)
案例总结
BitEngine 上线应用后,通过大量调优,在广告人群预估业务上获得了良好收益。将来,BitEngine 将持续加强性能以撑持广告业务场景,包含:引擎集成数据编码,使编码对用户通明;提供细粒度的缓存以缓存局部反复表达式的计算结果;优化表达式解析等。
点击跳转 云原生数据仓库 ByteHouse理解更多