菜鸟供应链实时数仓的架构演进及应用场景
The following article is from Flink 中文社区 Author 贾元乔(缘桥)
摘要:在 Flink Forward Asia 大会实时数仓专场中,菜鸟数据&规划部高级数据技术专家贾元乔从数据模型、数据计算、数据服务等几个方面介绍了菜鸟供应链数据团队在实时数据技术架构上的演进,以及在供应链场景中典型的实时应用场景和 Flink 的实现方案。
数据模型。菜鸟最初使用的是需求驱动的、纵向烟囱式的开发模式,计算成本高且完全没有复用的可能性,同时也会导致数据一致性的问题;整个数据模型没有分层,业务线内部模型层次混乱,使得数据使用成本特别高。
实时计算。该部分使用的是阿里的 JStorm 和 Spark Streaming,大多数情况下,二者可以满足实时计算的需求,但是对于有些复杂的功能,如物流和供应链场景,实现起来不够简单,开发成本较高;同时很难兼顾功能、性能、稳定性以及快速的故障恢复能力。
数据服务。数据主要存储在 Hbase、MySQL 和 ADB 等不同类型的数据库中,然而对于很多运营人员来说,查询数据库的频率并不高,但使用数据库的成本较高,尤其针对一些 NoSQL 的数据库;也存在数据使用不可控,如热点阻断、权限控制以及全链路监控等问题。
数据模型升级
第一层是数据采集,支持多种数据库中的数据采集,同时将采集到的数据放入消息中间件中;
第二层是事实明细层,基于TT的实时消息产生事实明细表,然后再写入TT的消息中间件中,通过发布订阅的方式汇总到第三、四层,分别是轻度汇总层和高度汇总层。
第三层轻度汇总层适合数据维度、指标信息比较多的情况,如大促统计分析的场景,该层的数据一般存入阿里自研的 ADB 数据库中,用户可以根据自己的需求筛选出目标指标进行聚合;
而第四层高度汇总层则沉淀了一些公共粒度的指标,并将其写入 Hbase 中,支持大屏的实时数据显示场景,如媒体大屏、物流大屏等。
下图左侧是前面介绍的公共数据中间层,包括整个菜鸟横向的物流订单、大盘物流详情和公共粒度的一些数据,在此基础上菜鸟实现了预置公共分流,从物流订单、物流详情中拆分出个性化业务线的公共数据中间层,包括国内供应链、进口供应链以及出口供应链等。
基于已经分流出来的公共逻辑,再加上业务线个性化TT的消息,产出各业务线的业务数据中间层。
以进口供应链为例,其可能从公共业务线中分流出物流订单和物流详情,但是海关信息、干线信息等都在自己的业务线进口供应链的TT中,基于这些信息会产生该业务线的业务数据中间层。
计算引擎升级
Flink 提供的很多功能非常适用于解决供应链场景下的需求,菜鸟内部提炼了一套 Flink 的 SQL 语法,简单易用且标准化,大大提升了开发效率。
此外,Flink 内置的基于 state 的 Retraction 的机制可以很好地支持供应链场景下的取消订单、换配需求的实现;
后来推出的 CEP 功能使得物流、供应链中实时超时统计需求的实现变得更加简单;
AutoScaling 等自动优化的方案可以使得菜鸟省去了一些资源配置等方面的复杂性和成本;
半智能功能如批流混合等也较好地满足菜鸟业务的实际需求。
案例 1:基于 state 的 Retraction
一个问题是针对表中 LP3 订单,在开始的时候是有效的(18 分的时候“是否取消”应该是 N,表写错),然而最后该订单却被取消了(最后一行“是否取消”应该是Y,表写错),这种情况该订单被视为无效订单,统计的时候不应该考虑在内。
另外,配送公司的转变也需要注意,LP1 订单在 1 分钟的时候计划配送公司还是 tmsA,而之后计划配送公司变成了 tmsB 和 tmsC,按照离线的计算方式(如 Storm 或增量)会得出右上角的结果,tmsA、tmsB 和 tmsC 与 LP1 订单相关的记录都会被统计,事实上 tmsA 和 tmsB 都未配送该订单,因此该结果实际上是错误的,正确的结果应该如图右下角表格所示。
案例 2:超时统计
用到的数据表如下图左侧所示,其中包含日志时间、物流订单号、出库时间和揽收时间。该需求如果在离线的小时表或天表中比较好实现,但是在实时的场景下,其实现面临一定的挑战。
因为如果仓出库后未被揽收,意味着没有新的消息流入,如果没有消息就没有办法进行超时消息的计算。
为了解决该问题,菜鸟从 2017 年初就开始了一系列的探索,发现一些消息中间件(如 Kafka)和 Flink CEP 等本身会提供超时消息下发的功能,引入消息中间件的维护成本比较高,而 Flink CEP 的应用会出现回传不准确的问题。
首先需要创建执行环境,构造 Process Function(访问 keyed state 和 times);
其次是 processElement 函数的编写,主要用于告诉 state 存储什么样的数据,并为每个超时消息注册一个 timerService,代码中 timingHour 存储超时时间,比如前面的提到六小时,
然后启动 timerService;
最后是 onTimer 函数的编写,作用是在超时的时刻读取 state 的数据,并将超时消息下发。
案例 3:从手动优化到智能优化
MiniBatch。原来每进来一条数据,就需要去 state 中查询并写入,该功能可以将数据进行聚合后再写入 state 或从 state 中读取,从而减轻对 state 的查询压力。
LocalGlobal。类似于 Hive 中 Map 阶段的聚合,通过该参数可以实现数据读取阶段的聚合,轻松应对 count 热点。
PartialFinal。面对更复杂的场景,比如 count_distinct 的热点,使用该参数可以轻松应对,实现两次聚合,类似于 Hive 中的两次 Reduce 操作。
大促场景:该场景下,菜鸟会提前预估该场景下的 QPS,会将其配置到作业中并重启。重启后 Flink 会自动进行压测,测试该 QPS 每个节点所需要的资源。
日常场景:日常场景的 QPS 峰值可能远远小于大促场景,此时逐一配置 QPS 依然会很复杂。为此 Flink 提供了 AutoScaling 智能调优的功能,除了可以支持大促场景下提前设置 QPS 并压测获取所需资源,还可以根据上游下发的 QPS 的数据自动预估需要的资源。大大简化了资源配置的复杂度,使得开发人员可以更好地关注业务逻辑本身的开发。
数据服务升级
案例 1:NoSQL to TgSQL
案例 2:跨源数据查询
一部分是已经计划好的离线 KPI 表;
另一部分是已经计算好的写入 Hbase 的实时表。
原本的实现方案是通过 Java 取两次接口,然后在前端进行加减乘除的计算操作。针对该问题,菜鸟提供了标准的 SQL,用针对跨数据源的查询,如 MySQL 离线表和 Hbase 实时表,用户只需要按照标准 SQL 的方式来写,通过升级的数据服务进行解析,再从对应的数据库中进行数据的查询操作。
案例3:服务保障升级
对于主备切换,前面提到的左右两侧分别是物理表和逻辑表的场景中,一个逻辑表可以映射成主备链路,当主链路出现问题时,可以一键切换到备链上;
此外,大促期间一些非常重要的业务,如大屏业务、内部统计分析等,会通过主备链路同时进行操作,此时完全读写其中一个库不合适,所期望的两条链路均有流量,而天工则实现了主备双活的功能支持,即将大流量切到主链,小流量切到备链;
当主链上受到其中一个任务影响时,该任务会被移到备链上;对于比较复杂、执行较慢的查询,会对整个任务的性能造成影响,此时会对这种类型的热点服务进行阻断。
其他技术工具的探索和创新
菜鸟实时数仓未来发展与思考
菜鸟之后发现 Flink 提供的 batch 功能可以很好地解决该问题,具体来讲是定义 TT 的 source,作为三天的实时场景的应用,TT 数据写到离线数据库进行历史数据备份,如果存在重启的情况,会读取并整合离线的数据,即使 Flink 的 state 丢失,因为离线数据的加入,也会生成新的 state,从而不必担心双十一的订单如果在十七号签收之前重启导致无法获取十一号的订单信息。
当然,在上述问题的解决上,菜鸟也踩了很多的小坑。其中的一个是整合实时数据和离线数据的时候,数据乱序的问题。菜鸟实现了一系列的 UDF 来应对该问题,比如实时数据和离线数据的读取优先级设置。
菜鸟也期望在实时 ETL 过程中的一些场景中,比如去重,也使用 Flink 相应的智能化解决方案来进行优化。
此外,在数据服务保障上,如主备切换等,目前仍然依赖人工对数据库进行监控,菜鸟也期望 Flink 之后能提供全链路实时保障的策略。
最后是业务场景的智能化,阿里 Alink 对于业务智能化的支持也是之后探索的方向。
推荐阅读
数仓社区
如有收获,请划至底部,点击“在看”,谢谢!