开源案例丨基于 Apache Doris 的有道精品课数据中台建设实践
2021.02.23 17:51浏览量:833简介:向大家分享有道精品课数据中台的架构演进过程,以及 Doris 作为一个 MPP 分析型数据库是如何为不断增长的业务体量提供有效支撑并进行数据赋能的。
满血版DeepSeek,从部署到应用,全栈都支持
快速部署、超低价格、极速蒸馏、应用开发、即时调用
本文旨在向大家分享有道精品课数据中台的架构演进过程,以及 Doris 作为一个 MPP 分析型数据库是如何为不断增长的业务体量提供有效支撑并进行数据赋能的。内容分享逻辑首先从实时数仓选型的经验为切入点,进一步着重分享使用 Doris 过程中遇到的问题以及 Doris 技术团队针对这些问题所做出的调整和优化。
1、背景
1.1 业务场景
根据业务需求,目前有道精品课的数据层架构上可分为离线和实时两部分。
离线系统主要处理埋点相关数据,采用批处理的方式定时计算。
而实时流数据主要来源于各个业务系统实时产生的数据流以及数据库的变更日志,需要考虑数据的准确性、实时性和时序特征,处理过程非常复杂。
有道精品课数据中台团队依托于其实时计算能力在整个数据架构中主要承担了实时数据处理的角色,同时为下游离线数仓提供实时数据同步服务。
数据中台主要服务的用户角色和对应的数据需求如下:

-
运营/策略/负责人主要查看学生的整体情况,查询数据中台的一些课程维度实时聚合数据
-
辅导/销售主要关注所服务学生的各种实时明细数据
-
品控主要查看课程/老师/辅导各维度整体数据,通过 T+1 的离线报表进行查看
-
数据分析师对数据中台 T+1 同步到离线数仓的数据进行交互式分析
1.2 数据中台前期系统架构及业务痛点

如上图所示,在数据中台 1.0 架构中我们的实时数据存储主要依托于 Elasticsearch,遇到了以下几个问题:
-
聚合查询效率不高
-
数据压缩空间低
-
不支持多索引的 join,在业务设计上我们只能设置很多大宽表来解决问题
-
不支持标准 SQL,查询成本较高
2、实时数仓选型
基于上面的业务痛点,我们开始对实时数仓进行调研。当时调研了 Doris, ClickHouse, TiDB+TiFlash, Druid, Kylin。
OLAP引擎 | 优势 | 劣势 |
Doris | 1. 兼容MySQL协议 2. 支持Online Schema Change 3. 支持更新 4. 集群扩缩容自动化 5. 支持基于时间分区,冷热数据分离 | 1. 开源较晚,目前还在孵化中 |
ClickHouse | 1. 单机性能强劲 2. 向量化引擎 3. 数据压缩空间大 | 1. 不支持标准SQL 2. 集群扩缩容不能自动Rebalance 3. 对更新支持不好 4. 运维成本较高 |
TiDB+TiFlash | 1. 兼容MySQL协议 2. 向量化引擎 3. 业务数据和分析数据同步方便(内部Raft同步) | 1. TiFlash不开源 2. 落地公司较少 3. 架构主要面向TP场景 |
Druid | 1. 基于时间分区,聚合数据查询较快 2. 支持冷热数据分离 | 1. 不支持明细数据存储 2. 不支持标准SQL |
Kylin | 1. 支持标准SQL查询 2. 支持预聚合 3. 社区发展较好 | 1. 依赖较多 2. 明细查询支持较弱 3. 资源消耗较多 |
于起初我们数据中台只有两名开发,而且存储相关的东西需要自行运维,所以我们对运维的成本是比较敏感的,在这一方面我们首先淘汰了 Kylin 和 ClickHouse。
在查询方面,我们的场景大多为明细+聚合多维度的分析,所以 Druid 也被排除。
最后我们对聚合分析的效率方面进行对比,由于 Doris 支持 Bitmap 和 RollUp,而 TiDB+TiFlash 不支持,所以我们最终选择了 Doris 来作为我们数据中台的主存储。
3、基于 Apache Doris 的数据中台 2.0
3.1 架构升级
在完成了实时数仓的选型后,我们针对 Doris 做了一些架构上的改变以发挥它最大的作用,主要分为以下几个方面:
-
Flink 双写
将所有 Flink Job 改写,在写入 Elasticsearch 的时候旁路输出一份数据到 Kafka,并对复杂嵌套数据创建下游任务进行转化发送到 Kafka,Doris 使用 Routine Load 导入数据。

-
Doris on ES
由于之前我们的实时数仓只有 ES,所以在使用 Doris 的初期,我们选择了通过 Doris 创建 ES 外表的方式来完善我们的 Doris 数仓底表。同时也降低了查询成本,业务方可以无感知地使用数仓底表。
具体查询 Demo 如下所示,我们通过学生的基础信息 Join 各种练习信息,对学生数据进行补齐。

-
数据同步
原来我们使用 ES 的时候,由于很多表没有数据写入时间,数据分析师需要每天扫全表导出全量数据到 Hive,这对我们的集群有很大压力,并且也会导致数据延迟上升,我们在引入了 Doris 后,对所有数仓表都添加 eventStamp, updateStamp, deleted 这三个字段。
-
eventStamp:事件发生时间
-
updateStamp:Doris 数据更新时间,在 Routine Load 中生成
-
deleted:数据是否删除,由于我们很多实时数仓需要定时同步到离线数仓,所以数据需要采取软删除的模式
数据对下游同步时可以灵活的选择 eventStamp 或者 updateStamp 进行增量同步。
数据同步我们采用了多种方式,通过 Hive 表名后缀来决定不同同步场景:
-
_f:每天/每小时全量同步,基于 Doris Export 全量导出
-
_i:每天/每小时增量同步,基于 Doris Export 按分区导出/网易易数扫表导出
-
_d:每天镜像同步,基于 Doris Export 全量导出

-
指标域划分/数据分层
将 Elasticsearch 中的数据进行整理并结合后续的业务场景,我们划分出了如下四个指标域:

根据上面的指标域,我们基于星型模型开始构建实时数仓,在 Doris 中构建了 20 余张数仓底表以及 10 余张维表,通过网易易数构建了完整的指标系统。
-
微批生成 DWS/ADS 层
由于我们多数场景都是明细+聚合数据的分析,所以我们基于 Doris insert into select 的导入方式,实现了一套定时根据 DWD 层数据生成 DWS/ADS 层数据的逻辑,延迟最低可以支持到分钟级,整体的多层数仓表计算流程如下图:

对于明细数据在 TiDB 或者 ES 的,我们选择了在 Flink 中进行窗口聚合写入到下游 Doris 或者 ES 中。而对于明细数据只在 Doris 单独存在的数据,由于我们大部分使用了异步写入的方式,所以数据无法立即可读,我们在外围构建了支持模版化配置的定时执行引擎,支持分钟/小时级别的扫描明细表变更写入下游聚合表,具体模版配置如下图:

需要对监听的源表以及变更字段进行配置,在配置的 interval 时间窗口内多个源表进行扫描,然后将结果进行 merge 后生成参数,根据配置的 threshold 对参数进行拆分后传入多个 insert sql 中,并在每天凌晨进行 T+1 的全量聚合,修复微批计算的错误数据。
具体的计算触发逻辑如下图:

-
数据血缘
我们基于拉取 Routine Load 和 Flink 数据以及服务上报的方式实现了数据中台完善的数据血缘,供数据开发/数据分析师进行查询。
由于我们的 Flink 开发模式为提交 jar 的形式,为了获取到任务的血缘,我们对每个算子的命名进行了格式化封装,血缘服务定时的拉取/v1/jobs/overview 数据进行解析,我们将不同算子的格式命名封装为以下几种:
-
Source:sourceTypeName [address] [attr]
-
Sink:sinkTypeName [address] [attr]
具体的血缘服务逻辑如下图所示:

通过血缘服务内部的解析后,批量地将血缘数据拆分成了 Node 与 Edge 存储到了 NebulaGraph 中,前台服务进行查询即可获得如下图所示的一条完整血缘:

3.2 数据中台 2.0 架构
基于围绕 Doris 的系统架构调整,我们完成了数据中台 2.0 架构
-
使用网易易数数据运河替换 Canal,拥有了更完善的数据订阅监控
-
Flink 计算层引入 Redis/Tidb 来做临时/持久化缓存
-
复杂业务逻辑拆分至 Grpc 服务,减轻 Flink 中的业务逻辑
-
数据适配层新增 Restful 服务,实现一些 case by case 的复杂指标获取需求
-
通过网易易数离线调度跑通了实时到离线的数据同步
-
新增了数据报表/自助分析系统两个数据出口
-

4、Doris 带来的收益
1. 数据导入方式简单,我们针对不同业务场景使用了三种导入方式
-
Routine Load:实时异步数据导入
-
Broker Load:定时同步离线数仓数据,用于查询加速
-
Insert into:定时通过 DWD 层数仓表生成 DWS/ADS 层数仓表
2. 数据占用空间降低,由原来 Es 中的 1T 左右降低到了 200G 左右
3. 数仓使用成本降低
-
Doris 支持 MySQL 协议,数据分析师可以直接进行自助取数,一些临时分析需求不需要再将 Elasticsearch 数据同步到 Hive 供分析师进行查询。
-
一些在 ES 中的明细表我们通过 Doris 外表的方式暴露查询,大大降低了业务方的查询成本。
-
同时因为 Doris 支持 Join,原来一些需要查询多个 Index 再从内存中计算的逻辑可以直接下推到 Doris 中,提升了查询服务的稳定性,加快了响应时间。
-
聚合计算速度通过物化视图和列存优势获得了较大提升。
5、上线表现
目前已经上线了几十个实时数据报表,在线集群的 P99 稳定在 1s 左右。同时也上线了一些长耗时分析型查询,离线集群的 P99 稳定在 1min 左右。

发表评论
登录后可评论,请前往 登录 或 注册