导读:知乎为实现精细化经营,进步经营效率,依赖 Apache Doris 构建了外部对立的经营剖析平台——舰桥平台,次要利用于事实接入层、事实建模层和事实运算层等架构核心层的建设,并继续对导入、查问等方面进行性能调优,最终实现上千亿行数据分钟级导入,千亿级数据秒级查问响应。该平台以后曾经广泛应用于知乎不同事业部的社区、商广、教育 & 会员、技术中台等畛域,失去各局部宽泛认可。
作者 | 知乎舰桥平台 Leader 侯容
在长期的业务经营中,知乎团队发现在内容经营、创作者经营、热点经营等许多场景中,经营团队须要依赖 SQL 或自行编写 SQL 代码来对用户信息、业务数据进行查问剖析。这往往须要投入大量的精力,造成人力投入大、工作效率低等问题,无奈实现精细化经营,无奈高效实现业务指标。
为了解决上述问题,知乎舰桥平台应运而生。舰桥平台是知乎外部对立的经营剖析平台(即一站式内容 & 用户治理平台),次要利用于知乎的六大外围经营场景,包含找人、找内容、盯人、盯内容、找机会、查问题场景。该平台以后曾经广泛应用于知乎不同事业部的社区、商广、教育 & 会员、技术中台等畛域。
知乎舰桥平台的根底能力包含筛选、剖析、打包和监控,这些能力都不同水平地依赖 Apache Doris 提供的计算、存储和剖析能力。在本文中,咱们将次要介绍 Doris 在舰桥平台中的利用,以及在 Doris 的优化实际。
业务架构
如业务架构图所示,知乎舰桥是一个数据密集型的一个利用,架构共由五层组成,这里对较为重要的层级进行介绍:
- 数据层和事实层: 数据层次要由内容数据、用户数据和流量数据组成,思考到原始数据不具备可展现性和可描述性,因而咱们将原始数据形象出内容事实、用户事实和流量事实,并存储在事实层以供下层利用。
- 根底能力层: 在根底能力层咱们搭建筛选、打包、剖析和监控四大根底能力。一般来说先依照业务要求筛选出指标用户数据,接着对这些用户进行下载并打包,打包后造成内容池、人群包或畛域(可别用于投放、举荐、Push、推送等场景);同时咱们提供了基于筛选、打包、全栈等的多维度面向剖析的能力,具体体现为榜单、散布、趋势、明细等;除此之外还提供了监控的能力,包含实时 / 定时监控、监控模板和监控合作。
- 业务工具层: 咱们将根底能力层的四大根底能力搭建成不同的业务工具,别离为榜单 & 列表、业务剖析、异动发现和问题诊断,用于反对业务侧的不同口头。
基于业务架构,咱们思思考应该通过一个怎么的技术架构能够低成本、高效率的实现咱们的需要,因而咱们先对技术架构进行了模块职责的划分,并心愿各模块可具备以下能力:
- 人机(UI)界面:以用户体验为核心,构建高效易用、简略易懂的 UI 界面,帮忙经营同学疾速了解并上手。
- 合作能力:针对多场景、多部门的业务需要,构建对立齐备的合作平台,最大水平地升高业务交互老本。
- 外围业务能力:须要将数据进行业务形象,确保所有需要都在已知的概念中被定义,方便使用、升高应用老本。
- 事实运算:须要反对大规模数据的高效低提早简单运算,以满足各业务线的经营需要。
- 事实建模:断绝接入层与业务层,进步迭代效率,以便更快地满足业务需要。
- 事实接入层:反对海量数据导入,并可能实现大量数据疾速导入,同时反对麻利接入,低成本扩大,以适应业务的疾速倒退和变动。
技术架构
为了建设符合要求和指标的技术架构,咱们对多个大数据组件进行了调研选型,在调研中发现,Apache Doris 各方面能力都比拟优良,能够提供多种数据导入计划、领有便捷易用的建表能力、更灵便的的物化视图以及对向量化的全面反对,基于这些优异性能,最终咱们决定引入 Apache Doris 建设舰桥平台技术架构,并被次要利用在舰桥平台的三个核心层,即事实接入层、事实建模层和事实运算层。
- 事实接入层: 事实接入层采纳了 Segment 文件预处理技术和大规模导入技术,极大水平地减速数据从 HDFS 导入到 Doris 的速度,在此过程中,广泛应用了 Spark 技术,此外咱们还通过 Flink 间接将另一部分数据流式写入,数据流式写入有两个步骤:一部分通过 Flink Connector 间接写入的,另一部分先通过 Flink 实现 ETL 解决,再通过 Routine Load 实现写入。该层借助于 Doris 丰盛的 Load 协定,实现了多种数据的规模化疾速导入。
- 事实建模层: 事实建模层咱们对业务进行了梳理和拆分,搭建了适合的业务模型,包含用户模型、内容模型、流量模型等等,同时还包含业务场景化的模型,例如主题模型和分层模型等等。因为 Doris 具备数据结构治理简略的个性,能够帮忙咱们疾速试错和优化数据模型,极大水平的晋升了数据模型迭代的效率。
- 事实运算层: 事实运算层咱们采纳了数据和机器预绑定的技术,并 利用了 Doris 的向量化技术和物化视图。此外,咱们还进行了大量的调优工作,例如,查问打算的调优、数据结构优化、算子合并技术等,从而实现性能的优化。
在基于 Doris 的事实接入、事实建模和事实运算层的反对下,咱们高效地搭建了外围业务能力、人机界面和合作能力,最大水平地满足业务需要,充沛达成了业务架构提出的指标。因本文以介绍 Doris 的利用为主,其余层的将不做具体形容。
优化实际
大量数据疾速查问
在人群圈选和筛选场景中,咱们须要解决大规模的数据,包含 240 万个标签、千亿级别的对象和标签量的关联数据,同时,咱们须要在极短时间内实现查问操作,通常要求在 1s 内返回查问后果,10s 内实现数据打包,时效要求十分高。那么怎么能够实现大量数据的疾速查问呢?
步骤 1:分而治之
分而治之的核心思想是将整体数据的与或非,转化为分组与或非后的合集。如果将它变成了一种倒排的 Bitmap,就能变成绘图的交并差。
咱们发现整体数据的交并差等价于先对某一个分组数据交并差、再进行合并操作。在这个根底上如果先将整个 Bitmap 取出实现交并差,实际上能够了解为只有一个线程在运算(理论不是),基于该发现咱们能够先将每一个数据进行交并差,这样就能够将其拆分成与分组雷同数量的线程或队列进行计算,计算完再由一个队列来进行数据合并。
优化前个别是在一个存储区中存储所有的特色,每个特色散布在不同的机器上,而在上述思路的驱动下,咱们批改了分组策略,先将人群特色分为许多小的分组,并将特色随机散布在不同机器上进行计算,通过该操作最终实现了速度的显著晋升。
以用户筛选为例:
- 通过将用户 id 分组,如每 100 万 id 为一组,设置一个 group\_id。
- 将该分组下不同用户特色、标签对立指向分组 group\_id。
- 先在每一个分组中计算特色、标签计算的与或非(即并差)。
- 当分组数据实现计算后,最初进行数据汇总。
- 同时开启多线程模式,晋升每组的计算效率
然而,在这个过程中,咱们又遇到了第二个问题,即特色计算带来了十分大的网络开销。这是因为各个特色随机散布在不同的机器上,这就导致在一个机器上实现了一部分特色运算,而后执行 Shuffle 进行数据交换,再进行第二次运算,再替换进行第三次运算,以此类推,假如条件十分多,网络开销就会十分大。
步骤 2:数据机器预绑定
咱们摸索并发现 Doris 的 Colocate 原理能够无效解决该问题,利用 Colocate 能够缩小数据 Shuffle 的次数,从而缩小运算的次数。因而咱们咱们尝试应用对数据分布和机器进行预绑定,数据机器预绑定利用了 Doris 底层的 Colocate 原理。
咱们将某一个分组 Key 和机器进行绑定,当数据与该分组 Key 绝对应,该数据将存在某一台机器下面,从而实现数据和机器的预绑定。通过该形式能够防止在特色计算中呈现频繁网络交互和数据混洗操作,从而大幅升高网络开销。
如下图所示为优化前的流程,数据进行不停的替换,查问打算十分高,网络开销十分大。
下图为利用 Doris 的 Colocate 原理进行优化的后果,能够发现查问打算相比拟之前少了很多,简略数据处理后即可实现,同时速度也十分快,次要归功于查问打算的升高占用了比拟少的网络开销。
步骤 3:算子合并
在解决网络开销问题之后,咱们开始思考如何减速执行的效率,因而咱们引出了算子合并(非官方命名)这一概念。其原理是应用更简单的函数代替原先简略的函数组合,在这个过程中,咱们与 SelectDB 团队和 Apache Doris 社区与进行了屡次沟通及配合,将日常应用的函数组合进行开发和落地,将合并组合好的函数进行上线应用。以下为拼接函数组成介绍:
bitmap_and_count == bitmap_count(bitmap_and(bitmap1, bitmap2))
bitmap_and_not_count == bitmap_count(bitmap_not(bitmap1, bitmap_and(bitmap1, bitmap2))
orthogonal_bitmap_union_count==bitmap_and(bitmap1,bitmap_and(bitmap2,bitmap3)
比方咱们须要进行一个数据查问,用简略的函数和简单的函数解决流程如下图所示:
- 简略函数:先查出数据,再执行
bitmap_and
,两头存储,执行bitmap_not
,再进行两头存储,最初执行bitmap_count
,输入后果。能够看出解决流程很长、速度很慢。 - 简单函数:如果应用合并后的函数
bitmap_and_not_count
,当咱们间接将数据输出到这个函数里,就能够输入后果。输入速度相比之前大幅晋升,从而晋升了查问效率。
大量数据疾速导入
在离线导入场景中,由 Hive 实现大量数据计算,这些数据文件写入到 HDFS 中,咱们将定期通过 Broker Load 将 HDFS 中的的数据拉取到 Doris 里。在这个过程咱们发现,在限定的集群资源下,当遇到大数据量导入操作,Broker Load 则会呈现超时。
经排查发现 Doris 从 HDFS 拿到 Parquet 之后,须要先进行解压缩,再进行分桶数据传输,最初通过排序、聚合、再压缩等一系列操作生成 Segment 文件,而这些过程都会在 Doris BE 上进行,同时咱们还会在此基础上进行 Bitmap 操作,从而导致 CPU 压力增大。
通过摸索,咱们发现 Spark Load 能够很好解决该问题,Spark Load 能够将导入拆分为计算和存储两局部,将分桶、排序、聚合、压缩等计算逻辑放到 Spark 集群,产出后果写到 HDFS,Doris 再间接从 HDFS 中拉取后果文件写到本地盘。
- Broker Load:BE 节点负责计算,算力取决 BE 节点个数及配置。
- Spark Load:Spark 集群负责计算,算力取决于集群配置,且弹性强。
咱们将 Segment 文件预处理移至 Spark 后,速度有了显著的晋升。以后 1.2 TB、1100 亿 + 行数据,导 入工夫从 9 小时缩短为 55 分钟,速度大幅晋升,其中 Doris 的应用工夫缩短到了 20 分钟,另外 35 分钟在 Spark 集群上,无效升高了 Doris 集群负载。
在摸索海量数据疾速导入的过程中,咱们遇到了一些问题,并胜利地解决了它们。在这个过程中,咱们积攒了许多贵重的教训和解决方案,当初将这些教训和计划分享给大家,心愿能为大家提供帮忙。
HDFS 权限认证问题:
知乎以后的 HDFS 是应用 Symbol 形式认证的,这与很多其余公司不同。咱们发现,Spark Load 命令解决完后,将转发到 Spark Launcher,再由 Spark Launcher 执行 Spark Submit 命令。在这个过程中,不会传递环境变量,因而咱们无奈将用户名和明码传递给 Spark Submit 再执行,并且也无奈将它们配置到环境变量中。而在理论场景中,咱们须要应用不同的用户名和明码来读取不同的数据进行导入,因而,咱们减少了动静设置和环境变量等性能来解决这个问题,目前相干 PR 合并到了社区中。相干 PR:https://github.com/apache/doris/pull/12276
Doris 拉取 Spark 产物速度慢
在 Spark 实现计算之后,咱们发现 Doris 拉取产物的速度比较慢的问题,通过进一步跟踪发现当在解决小规模数据时,可能在一分钟内解决完一个文件,但当数据规模变大时,则须要破费五分钟能力解决一个文件。那么是否能够通过调高工作数来进步速度呢?于是咱们依据线上理论的超时状况和导入速度要求,最终决定将下方参数从 3 减少到 9,后果发现速度立刻失去了显著的晋升。
push_worker_count_high_priority
:改为 9。push_worker_count_normal_priority
:改成 9。
参数调整后不仅大幅晋升了拉取速度,单个 BE 写入速度达到 120MB/s,IO 和 CPU 资源也失去了更充沛的利用。
通过这次调参咱们发现,大家能够依据理论需要来调整以下三个参数,以解决拉取产物速度较慢的问题:
push_write_mbytes_per_sec
:BE 磁盘写入限速。push_worker_count_high_priority
: 同时执行的 Push 工作个数。push_worker_count_normal_priority
: 同时执行的 Push 工作个数。
隐式转换改为显示转换
在应用 Doris 向量化版本的过程中,因为咱们有很多基于 Bitmap 表的计算,在应用隐式转换时会呈现无奈导入 Bitmap 表的问题。为了解决这个问题,咱们禁止隐式转换并开启了显式转换,并将相干的 PR 合并到了社区中。相干 PR:https://github.com/apache/doris/pull/12394/files
Spark 聚合速度 慢
因为数据存在歪斜,导致在 Spark 数据聚合速度比较慢,基于此,咱们从新依照离线计算的一个 Key 来进行分组,新增一个 Bucket 列,以解决数据歪斜导致计算速度慢的问题。
并发 数量限度
咱们在 Spark Load 的 Spark DPP 代码中发现:在 stage 2 的过程中,工作的并行下限为 200,这导致在面对数据量十分大的工作时,写入速度十分慢。为解决这个问题,咱们减少了自适应的并发数,并将相干的 PR 合并到了社区中。相干 PR:https://github.com/apache/doris/pull/12186
性能晋升
Apach Doris 1.1 版本实现了计算层和存储层的全面向量化、正式将向量化执行引擎作为稳固性能进行全面启用,性能较之前版本有 3-5 倍的微小晋升;并在 1.2 版本所有模块都实现了向量化,包含数据导入、Schema Change、Compaction、数据导出、UDF 等,查问性能较非向量化版本大幅晋升。因而在 1.1 向量化版本推出后,咱们针对某些重要场景进行向量化迁徙,并次要逐渐在所有场景中利用。
当咱们从 0.15.3 迁徙到 1.1 版本之后,给业务带来非常明显的收益,大多数场景均能达到 5 倍以上 的响应速度晋升,个别场景响应速度甚至能够达到非向量化版本的 10+ 倍,咱们别离对以下 7 个场景的查问耗时进行了比照。
- 场景 1:简略(数百)圈人条件,百万级别 Bitmap 人群打包
- 场景 2:简单(数千)圈人条件,上亿级别 Bitmap 人群打包
- 场景 3:多维度(6 种)筛选、单表查问、单日期指标宽表、数据聚合 SUM,单日数据量 1.8 亿 +
- 场景 4:(6 种)筛选、单表查问、多日期指标宽表(周期:15 天)、数据聚合 SUM,单日数据量 1.8 亿 +
- 场景 5:单表查问、COUNT 计数,单日数据量为 1.8 亿 +
- 场景 6:多表查问,A、B 各表数据量为 1.8 亿 +、1507 万 +。A 表波及每天数据 SUM 聚合、COUNT 聚合,B 表波及 Bitmap 聚合,A、B 先聚合再与 C 表 Join,子表再顺次 Join,Join 次数共为 6 次。
- 场景 7:5 亿 + 数据明细剖析及单表查问
将来瞻望
在技术上 咱们将在查问和写入方面进行优化,在查问方面 将实现图引擎,现阶段的业务场景次要通过 Doris OLAP 和 Doris On ES 实现了多维分析和全文检索,将来随着业务倒退,关系场景将越来越多,基于此咱们将尝试通过 Doris 裁减图引擎,最终在多维分析和全文检索的根底上实现与图引擎的联合。依据近期社区动静得悉,Doris 对图数据库 Nebula Graph 反对的 PR 曾经就绪,将在将来版本中正式公布(相干 PR:https://github.com/apache/doris/pull/19209)。在写入方面咱们将实现 Spark Load 底层解耦,Spark Load 底层实现时,目前 Doris 和 Spark 是耦合的,导致在应用时有诸多不便、无奈大规模应用。将来咱们打算将 Spark 和 Doris 解耦,不须要 Doris 来提交工作就可间接在 Spark 提交生成产物 Doris Segment 数据文件,实现后告诉 Doris 下载 Segment。
在业务上, 咱们打算与试验平台开展单干,将指标制订及实现的判断从人工把控转变为主动配置试验和验证。同时咱们也将进行业务插件化能力建设:
- 插件化架构落地,联结业务提供绝对欠缺的产销联动工具链。
- 将原有通过人工保护的流程,以工具链的模式配置,充分发挥经营同学的外围竞争力,整体升高业务老本。
新版本尝鲜
Apache Doris 在 2.0 Alpha 版本(https://github.com/apache/doris/releases/tag/2.0.0-alpha1)中曾经实现了单节点数万 QPS 的高并发点查问能力、高性能的倒排索引、基于对象存储的冷热数据拆散、基于代价模型的全新查问优化器以及 Pipeline 执行引擎等,欢送大家下载体验。
为了让用户能够体验社区开发的最新个性,同时保障最新性能能够播种到更广范畴的应用反馈,咱们建设了 2.0 Alpha 版本的专项反对群,请大家戳此填写申请,欢送宽广社区用户在应用最新版本过程中多多反馈应用意见,帮忙 Apache Doris 继续改良。