应用实践 | 知乎基于 Apache Doris 的 DMP 平台架构建设实践
关注「SelectDB」第一时间获取更多资讯!
导读:知乎基于业务需求搭建了 DMP 平台,本文详细的介绍了 DMP 的工作原理及架构演进过程,同时介绍了 Apache Doris 在 DMP 平台的应用实践,本文对大家了解 DMP 工作方式很有帮助,欢迎阅读。
作者|用户理解&数据赋能研发 Leader 侯容
DMP 业务背景
DMP 平台是大家老生常谈的话题。在早期广告系统出现之后就拥有了类似的 DMP 平台,比如:腾讯的广点通、阿里巴巴的达摩盘等都是业界做的比较好的 DMP 平台典型。而知乎搭建属于自己的 DMP 平台,一方面是因为知乎有相关的站内运营业务;另一方面也是因为我们可以通过搭建 DMP 平台支持内部系统对接、同时还可以协助完成相关业务发展以及定制化需求建设的目的。
>>> 业务介绍 DMP 业务包含:业务模式、业务场景以及业务需求。
>>> 业务模式
从站外转站内。典型场景是广告主在进行广告投放的过程中,如何通过 Mapping 将可能出现的站外人群转到站内,并在站内的系统上承接这些用户包。 从站内转站外。在知乎内先找到定向用户后再去用这些用户在三方投广告。 站内运营。包括内容运营,用户运营以及活动运营。一方面可以增加知乎相关内容的宣传,另一方面进行客户定位并精准解决某些客户的问题与需求。与此同时,我们也可以通过活动设计来提升业务效果。
信息流方面。拿推荐场景举例:推荐场景中会有定向推荐以及定向提权两种诉求。定向推荐是我们把推送内容定向推送给某些用户,而定向提权是我们把推送内容在被推送的用户身上完成提权并重新打分。 广告侧实时竞价。得知该用户身上挂了哪些广告之后可以进行实时竞价,通过排序选出最适合该用户的广告。 详情页。详情页中会有弹窗提示:比如说某个用户点击某个详情之后,若该用户没有达到目标条件,会弹窗引导来该用户达到条件。 活动平台。设置活动的目标用户。针对不一样的目标群体,展示不同的活动信息。 触达系统。比如在推送消息、弹窗和短信时,可以拿到一类具体的用户,之后向该类用户进行发布相应的 Push 和站内信等。 站外投放。找到合适的用户群并在站外为其投放相应的广告。
基于业务模式场景,在人群方面能做的事情可以分为三类:
对接系统
该用户命中了哪些人群包。拿广告系统为例,该人群包 ID 可以 Mapping 成一个广告,也就是该用户命中了哪些广告。 内部人群包。人群包对内部而言就是把内容推荐给谁,或者给谁发布内容的 Push。 对外部的广告。当我们筛选出一类用户需要投放在站外时,这时候就是在使用对外部的人群包。对于这两个人群包之间的区别而言,人群 ID 会有不同:一种是站内的通用 ID,另外一种是基于不同投放平台上对应的对外 ID。
人群定向
人群泛化,拿到比较小的种子人群包后,基于规则寻找相似特征,再通过对相似特征的置信度进行调整,扩展更多的人群。 用户量预估,选中一批用户后需要立即了解这批用户的数量有多少。
人群洞察
包括画像洞察,用户的内部画像以及两个不同人群包之间的对比分析。
站内运营自闭环
通过 AB 实验我们可以对前后的人群包再做一次对比并发布相关的 Push。如果点击量有所提升,我们在后续过程中就会不断的完成循环,最终找到基于我们运营场景的领域的精准用户。
站内向站外投放
基于已经积累的用户特征数据,找出在知乎内部有几率产生站外效果的人群,并划出该类人群的范围。再通过 Mapping,可以把站内的 ID 转换成在三方投放平台上产出的 ID 并进行投放。
由于这个过程我们的站内系统不同,并不能直接拿到相应的埋点数据供我们进行数据链路建设,所以就必须要通过三方投放平台上下载相应的埋点数据,通过类似的场景完成数据导入后再进行后续流程的建设。这也就导致了整个过程的效果回收会比较长。
站外转站内
假如我是一名知乎站外的广告主,我要投放一个牙膏类的产品,但是我对知乎的用户并不是特别了解。通过前期所做的运营调研,可以发现历史购买牙膏的人群包是什么样子。那么就可以把前期调研所得到的人群包通过 ID Mapping 转换为知乎 ID 并导入生成目标人群。但是广告主拿到购买牙膏的人群可能存在与知乎用户重合度较低的情况。这时候启用第二个功能,也就是人群泛化功能。
基于上述运营流程,我们可以抽象出 DMP 平台最核心的功能包括洞察、定向以及 ID mapping。
我们根据上述的用户画像,构建出了画像特征。其中标签是最重要的部分,也是离散部分。连续部分包括了用户的停留时长以及相关的用户行为,比如:某人在某地做了什么事等,这些都属于连续特征。特征方面,在该特征还没有打上标签之前,我们会统称为普通特征。
人群定向。人群定向方面整体上分为导入与导出、特征圈选以及人群泛化这三个功能。 人群洞察。包括构成分析和对比分析两种功能。构成分析部分我们可以简单理解为一个饼图或柱状图。对比分析是多个人群对比分析。 ID Mapping。整体上将无论是 oai、idfa、手机号,全部硬生成知乎的连续统一 ID,而且这个连续 ID 基本是严格自增的。 特征接入 建设方式分为实时特征及离线特征 标签组方面有离线和实时两种接入方式。其中树状标签主要用来应对复杂场景,如用户对某话题在阅读和互动方面的是多选的树形结构。
DMP 架构与实现
基于以上我对架构的认知,对业务以及整体 DMP 架构进行拆解:
平台方面,包括广告平台、信息流、广告引擎以及触达系统。 操作人员,包括运营、投放以及销售等业务相关操作人员。 诸如特征开发的产品及相关内部产品。
第三方面是对接我们的内部系统,这部分主要会降低我们日常开发的成本。
DMP 能够支持人群圈选、泛化、人群洞察的核心业务模块;支持标签生产, ID Mapping 还有计算任务运维和存储方面的功能。
DMP 业务模块分为上下两层,向上的业务层实现新增功能的低成本化,重点在于可扩展性;向下的业务层随着人群与业务功能的增长,整体的开发或技术投入成本不会有太大的产出,也就是资源上的可扩展性。
有人会问 Redis 成本是否会太高?不会的。因为核心的圈选人群逻辑都是在 Doris 上实现的,存放的大量相关标签都是通过 Doris 进行存放,只有在某个广告要指定某目标人群的某几个特征进行排列组合并且完成泛化时,我们会圈选出某个人群包 ID 对应的结果,最后才导出存放到 Redis 中。因此 Redis 的主要目的是用来扛高并发,实际的存放量很少。
业务向
人群预估:比如说对性别、年龄、感兴趣的话题、该用户手机品牌是等多个条件进行排列组合,要求能够在 1 秒内完成精确结构的人群特征量级预估。 人群圈选:经过精确结构的人群数量预估后,可以在分钟级别内将预估结果转化为要进行投放和使用的相关人群包。 人群包泛化:泛化的能力要求尽可能简单,比如说我选择有历史的人群包后,就可以进行人群泛化并有具体的执行度选择。
可以探索当前活动入口画像,并完成流量回收。比如说我向 100 万人发布了推送,其中有 3 万人点击,那么可以对这 3 万人进行流量回收,与已推送的 100 万人进行对比,就可以这 3 万人明显的用户特征,方便我们后续提取出更精准的用户群体。
基础向
在建设原子特征时,我们就需要从离线或实时数据中生产大量相同基准的特征。 对于派生特征,会基于已生产的特征再生产一个特征。举例:假如我们认为某群体是高消费能力群体,放在一个简单的场景中,我们可能会圈选出一位在 18-25 岁之间并在一二线城市的女性,并认为这样的特征可能是对化妆品消费能力比较高的群体特征。之后我们就会把该特征作为派生特征进行存储并去加快后续计算速度并降低运营筛选的成本。
特征建设可以做到能力隔离,以此来提升我们特征建设和上线效率。 ID Mapping 屏蔽了我们 ID Mapping 的困难成本。我们会分为完成原子特征建设、完成派生特征建设以及进行基础设施的建设这三部分。当基础设施建设同学完成屏蔽或在架构上隔离之后,特征建设的同学就不需要管 ID Mapping 方面的问题,只需要管专注于建设特征即可。 计算任务运维部分,对于业务开发同学并不需要知道底层到底发生了什么,为此我们要有一个同学完成对底层的封装后向上层提供一个接口,业务侧可以直接使用底层的功能的同时屏蔽了底层的复杂性。通过抽象与屏蔽,可以明显的提升最终上线与建设的效率,并能让其他某些工作从研发侧转移到运营侧。
举例:我们当前有两种特征,第一种是原子特征。在形成原子特征的过程中,写一个 SQL 就可以形成一个特征。分析师与业务产品均可以参与特征的建设过程。第二种是派生特征。我们在运营后台上具备派生特征的交并差的能力,一些业务上的运营动作可以直接在管理后台进行操作并完成派生特征的建设。这样主要的工作量从研发侧逐渐转移到了产品侧与业务侧,明显的提升了各种能力和特征上线的效率。
DMP 核心介绍
DMP 核心部分有两方面:数据的写入/导入以及快查/快读。写入和导入是链路及存储的一部分,快查和快读我会在后续进行介绍。
第一套是在 ElasticSearch 上的搜索标签存储。 第二套是在 Doris 上,也是最核心的存储。 第三套是整体 ID Mapping 的存储。
接下来为大家公布几个量级:用户X标签量级,为 1,100 亿;ID Mapping 是一个宽表,量级是8.5亿;ElastichSearch,量级是250万。这三个量级也是我们为什么选择 ElasticSearch 和 Doris 的原因。
上述的数据导入后形成 3 张表,这里是利用这 3 张表产生人群相关定向和人群包。
第一个是通过购物车筛选人群标签后进行人群预估,最后完成人群圈选回写到 Redis 的流程。 第二个是人群泛化,通过 AI 平台完成 AI 模型的整体训练及人群的推理,再回写到 Doris 中,通过置信度进行选择并打上标签。
人群泛化。人群泛化流程最开始可能会有上传人群包的过程,也有可能没有。这个过程主要解决有些业务中,我们拥有某些历史活动的人群并需要进行人群泛化的问题。如果说它的人群包之前点击过我们的 Push,可以直接筛选,筛选完成之后关联所有的用户特征进行用户训练,模型训练完成后再对全站用户进行推理,推理出一批带有置信度的人群 ID 的结果并返回写到 Doris 之中。在这个过程中会同时发起另外一个流程,此流程会对用户侧的泛化的结果进行筛选,可以根据合适的置信度选择合适的数量。
第二种是获取到历史效果后进行洞察和分析。包括查看用户的画像后再重新根据标签关系圈选,之后又叠加了一次历史正向人群包后再去进行泛化。泛化之后再实现分发条件,最后再进行圈选,将该人群包给广告与相关的投放业务。运营侧会做很多基于原子能力以外更复杂的一些组合后再进行使用。
背景
当前 DMP 系统中有两大功能,第一大功能是人群定向,另外一大功能是人群洞察。基于这两大功能会有一个底层的功能是建设各种用户方面的画像特征。当我们完成拆解之后,我们就会发现人群定向的这部分功能是运营侧或业务侧的痛点。
场景要求
人群预估,针对投放和营销场景,运营侧会有人数预期,那么会构建相应规模的购物车,持续在购物车中加入新的特征,需要立即看到新的特征加入之后会圈选出多少人,而不是每次加入新的特征后都需要很长时间的等待。 人群圈选,针对热点运营。运营侧在日常工作中会持续跟进发生的各种热点事件,当发生了某些热点事件后,要快速的圈选出人群包发布 Psuh 和 推荐。如果圈选过程需要好几分钟,就会错过热点事件。
难点
第一个数据量极大,如上图标注。 第二个期望时间很短,人群预估与人群筛选分别能够在一秒钟内和一分钟内完成。
性能优化(1)
倒排索引和按条件查询
首先,倒排索引方面,我们将查询条件由原先的 and or not 改成了 bitmap 函数的交并差;同时我们把连续数值打散成为了离散标签。举例:用户的年龄是大于 0,小于 100 的 int 型,如果按照数字顺序进行筛选,运营侧是不好把控的,圈选的过程中也会导致使用效果不理想。因此我们把按照顺序排列的年龄打上另外的标签,称为年龄段,比如 18-25,0-18 等。 接着,把原先的 and or not 的查询转换为了倒排索引的相关查询,原先建立的表就会变成按照 tag_group 、tag_value_id 、置信区间的标识、bitmap 的顺序排序。同时基于这部分我们也需要进行 ID Mapping,ID Mapping 在导入的过程中的核心就是要把用户 ID 变成连续自增的。
查询逻辑变更
底层 Doris 中用的是 brpc。在数据交换的过程中,因为每一个单一的 bitmap 都很大,就会导致 brpc 传输拥堵,有时甚至会出现上百兆的 bitmap 进行交换的情况。上百兆的 bitmap 进行交并差计算时性能很低,基本上我们想要达它达到 1 分钟圈选人群,1 秒钟进行人群预估是不可能的。
性能优化(2)
基于仍存在的问题,我们进行了第二阶段的优化。
分而治之
数据预置
算子优化
全部放到某一个物理机上之后,就可以把聚合的算子由原先 bitmap_and_not 的 bitmap not 和bitmap count 替换成一个函数来实现。此时基于 Doris 团队的新版本,增加了类似 bitmap_and_not_count 的组合函数后,性能相对于原先的嵌套函数有了比较明显的优化。
解决方案
由于把原先几个 bitmap 的计算变成了多个小组 bitmap 计算,能进一步的提升多线程的并行度,使计算速度提升;同时也对代码进行了优化,将可复合的 bitmap_and_or_not 函数在提交时合并成同一个函数;在写入过程中把分组 ID 和相应的百万分组进行写入调整。 离线和实时之中都会写相应的 tag 表。在完成 tag 表的写入之后可以把每一个 tag 之中不同的 user tag 写到不同的物理机上:比如可以将 300 万拆开分别写在三台不同的物理机上,完成物理机方面的区隔。这里借助了 Colocate group 以及 Group key 进行设置。完成写入之后,计算过程从原先的整体计算变成独立按照每一个 Group 进行计算。由于整体的 bitmap 很大,每一个独立的 Group 又都在一台物理机上面进行计算,速度有非常明显的提升。 在每一个 Group 计算之后进行合并,合并之后,人群预估变成了不同物理机上面的数字简单加和,结果基本达到秒出。人群圈选也就变成了不同物理机上面的 bitmap,再 Shuffle 出去做最后的合并,这个过程量级很小,可以做到 1 分钟之内输出结果。
优化结果
整个过程主要对数据进行了拆分,由 Doris 的 Colocate 原理把拆分后的数据提前预置在某一台物理机上面,通过优化,可以满足大部分场景的运营要求。
未来及展望
业务向
未来我们希望 DMP 运营平台不光是松耦合的模式,而且能够在在业务上执行强耦合、强绑定的模式。这样的运营模式在使用过程中会更舒服,可以完全在 DMP 平台上完成了整体运营流程,并可以根据运营效果设计相关的 AB 实验,不断优化。
技术向
第二个是导入速度。我们经过五天的时间,每天需要导入大概 2TB 的数据量,存储了11TB 的数据,数据量比较大,我们希望在导入的过程中可以进一步的提速。当前我们了解到业界有做 Spark 直接撰写具体 OLAP 引擎文件,我们也在思考是否可以通过 Spark 直接撰写 Doris Tablet 文件并挂载到 FE 上面,让我们能够快速完成导入或写入。
Q&A 环节
加入社区
欢迎更多热爱开源的小伙伴加入 Apache Doris 社区,参与社区建设,除了可以在 GitHub 上提 PR 或 Issue 之外,也欢迎大家积极参与到社区日常建设中来,比如:
最后,欢迎更多的开源技术爱好者加入 Apache Doris 社区,携手成长,共建社区生态。
相关链接:
SelectDB 官方网站:
https://selectdb.com
Apache Doris 官方网站:
http://doris.apache.org
Apache Doris Github:
https://github.com/apache/doris
Apache Doris 开发者邮件组:
dev@doris.apache.org