云音乐实时数仓建设以及任务治理实践

本文根据汪磊在【第十三届中国数据库技术大会(DTCC2022)】线上演讲内容整理而成。

本期分享嘉宾

汪磊

杭州网易云音乐科技有限公司

数据平台开发专家

【嘉宾介绍】汪磊,Zeppelin Contributor,云音乐数据平台开发专家,数据平台组计算平台负责人。2013年加入网易,参与过易信、云音乐等产品的数仓建设,目前主要负责云音乐离线、实时、机器学习等统一数据平台建设工作。

分享概要:

1.云音乐实时相关业务现状和数据规模

2.分区流表技术介绍

3.数据任务治理实践

4.未来规划

以下为大会演讲实录部分:

随着业务的发展,数据流量增大,以及实时数仓的应用越来越广泛,音乐实时数仓的流量越来越大,用户也越来越多,超大流量的消息队列对整体带宽资源、下游的消费任务的稳定性以及计算资源都带来了巨大的挑战。为了解决这一问题我们升级了Flink原生的实时流表的方案扩展实现了流表的分区支持,大大降低了整体的流量带宽和计算资源的消耗。

底层技术的升级带来了大量任务的升级改造、业务发展太快,平台需要下线的废弃数据任务也会越来越多,平台开发水平层次不齐、大量的数据任务配置,都需要优化升级等等,这些都是业务平台开发日常面临的繁琐、难以推进的且很难说明价值的工作。为了更好地触达用户,系统化解决日常的数据任务治理工作,我们设计了一套系统化、流程化、 可审计跟踪的的大数据任务治理方案来解决日常任务治理工作。

云音乐实时相关业务现状和数据规模

首先介绍下云音乐目前实时相关业务的一些现状和数据规模,如下图所示。

关于云音乐实时数仓整体的架构,其实业界基本都差不多。数据来源主要为两部分:

1.用户行为日志,从客户端收集,通过消息队列归档到数据仓库。2. 业务数据的库的数据,通过binlog订阅来实时同步业务库数据入仓。

实时数仓的建设大部分的点集中于以下两个部分:

第一部分即 ODS 到DWD明细层。明细层是实时数仓的基础,不做任何的聚合操作,将原始的用户日志清洗成设计好的数据统计模型;整合业务领域语言,扩展统一的业务统计指标,统一数据口径,避免数据不一致的问题,降低数据仓库使用门槛。

再上层即DWD 到DWS 聚合层,在实时数仓的建设中,DWS层聚合的成本非常高。实时计算大部分只会做一些很轻量级聚合,比如分钟级、小时级别的聚合,按需建设,有需求时会将这一层的数据落到现在比较流行的OLAP引擎当中,比如 CK,来满足一些业务定制化的实时的数据查询需求。

其次是ADS层,应用数据层。这层数据完全根据业务需求进行开发,比如实时的日活大盘,我们一般会查询DWD的数据,聚合UV数据,然后将结果写入到KV当中,供前端查询。

再比如在一些直播的Push场景中,当主播上线时,需要实时地查询主播的在线的粉丝,来发送push消息,这个查询需要多个业务库的数据表关联查询出结果,但是在生产环境中,为了保障在线服务的稳定性我们不可能直接从业务库执行很复杂的关联查询,而是需要通过binlog将业务库的数据实时同步到OLAP引擎中,通过OLAP引擎查询出最终的结果触达用户。这一场景是Binlog入仓在实时场景中的一个典型应用。

音乐相关业务现状和规模

由于用户基数越来越大,业务线越来越多,开发对实时平台的需求也越来越多,我们在平台建设中也遇到了诸多问题,以下几类较为典型:

1.开发门槛高

Flink 经过多年的发展,Flink SQL不断优化,Flink上手的门槛在降低,但是难于运维调优,SQL屏蔽了很多细节,遇到问题,用户往往无从下手。Flink本身的成熟度也还在发展当中,比如在打状态任务上,存在很多的稳定性问题。

这些年大数据领域的存储中间件层出不穷,如:Clickhouse、Drios、Kafka等等,都有着不小的学习成本和门槛,整体处于一个易上手难精通的状态,在音乐这种大的业务体量下,很多问题都会被暴露出来。

再加上还有一些用户背景的问题,我们用户很多都不是专门数据背景的,而是前后端的应用开发,在此背景下平台的使用和运维门槛问题更加凸显。

2.模型设计问题。

在实时数仓设计上,需考虑一些性能问题。若按照离线数仓直接设置,一些大的数据量的实时流表,会导致Kafka的压力非常大,带宽压力、下游任务的处理压力也会非常大。

所以还需权衡性能和模型设计上的一些问题,初期让人很是头痛。下文会讲到分区流表,它主要用来解决该问题。

3.任务运维问题。

任务指标:任务的 metrics 指标非常专业,很多用户理解起来门槛很高。比如Kafka、RocksDB 的一些指标,专业度非常高,难于理解。

任务的流量波动,外部存储的使用成本,机器的负载,其中任何一环出现问题,任务的稳定性都会受其影响,定位问题非常困难。

任务状态问题:我们不知道一个正常运行的任务 到底还在不在服务业务,因此任务治理上非常难,存在一定的问题。

4.任务治理问题。

比如离职员工任务问题,存在大量的离职僵尸任务;任务价值闭环问题,任务花费这么大的资源是否真的值得;资源配置和性能问题,任务的资源配置是否合理。

分区流表技术介绍

关于在实时数仓中遇到的一些问题,我们构建了分区流表技术以解决模型设计的性能问题。

分区流表技术建设

如果按照离线数仓的思想构建数仓模型,模型设计和任务稳定性之间需要权衡,以下就离线和实时数仓的一些区别加以对比。

离线:

  • 相比实时场景对数据量、资源消耗敏感度较低。
  • 优化手段多:分区、分桶、数据有序写入等,可以高效地过滤掉无效的数据。
  • 存储稳定性:HDFS等落地存储,数据量的增长对稳定性的影响相对较小。

实时:

  • 对任务延迟非常敏感,数据量任务的稳定影响较大,从而导致不任务SLA达不到业务需求。
  • 优化手段少:以KAFKA为基础的实时流表,几乎没有过滤数据的高效策略。
  • 存储稳定性:数据量的增长对KAFKA的稳定性影响较大,多一个消费,KAFKA性能/网络带宽就多一份压力。

总体而言,在离线场景上,大家更关注于业务模型的设计,对于性能的敏感度非常低。然而在实时上则不同,对于任务的延迟非常敏感,整体上很难做到很高效的过滤策略。

整体架构

我们的目标是希望让用户做到离线,能够以离线的模型达到业务的需求,同时又不牺牲太多性能,保证Kafka 的压力是可控的。

我们初期希望在 ODS到 DWD 层能够做一些优化,为了解决上述问题,牺牲了模型的统一性加以解决,毕竟性能、稳定是第一,初期还是以性能保障为第一要素。

问题的解决上,我们的方法是什么?各大公司也有相应方案,比如做一些日志分发,我们初期也是将 ODS 层的数据分发为不同的消息队列。

初期架构

比如离线里面有一个表叫 user action,实时的ODS层数据表的schema也是一样,但我们会将其分成比如Queue1、Queue2、Queue3…..Queue10,不同的消息队列存储着不同的业务主题的数据,看似有点奇怪,但它有效地解决了很多流量带来的性能问题,当然也存在着一些问题:

1. 使用门槛高。用户在用这些表时,会按不同的业务需求分成 10 个 topic。不同的队列里有不同的流表。用户在使用之时有一定门槛,首先需要查每一个表记录的是什么业务主题的数据。

2. 模型不统一。我们与离线的模型肯定是不统一的,导致用户在使用实时数仓和离线数仓有很大的不同,带来很多体验问题,也不方便后续批流一体数仓的建设。

3. 复用成本高。开发代价低,复用程度低,每个分发都要单独地构建消息分发的程序。

4. 运维调整困难。比如要想将 Queue10 流量大了再做一次分割,其成本实则很高。需要通知到所有下游的任务去重启任务,重新调整代码,整体成本太高。

分区流表

为解决上述问题,我们在2021年年底开启了分区流表的建设。

分区流表原理上很简单,希望做到在流表侧也能够有着比较好的手段做来有效的做数据过滤,减少数据的读取,所以我们联想到了Hive分区表的结构,希望在流表上也能做到和HIVE分区表一样的分区效果,减少流量的吞吐。

虽然做不到更高效地做到分桶或者有序的过滤这种策略,但是在分区上仍可做一做。因此我们按照不同的行为,按照表字段做了一些分区策略,不同行为日志分别存储到不同的topic当中。在流表的 case 上,一个流表对应多个分区,每个分区存储不到的业务数据,做到和离线模型统一,同时还能保证性能。

在用户去读的时候,我们会根据用户的查询条件进行分区下推,拿到对应的 topic,减少不必要的数据读取。

流表分区的功能直接集成在我们的Source 和 SINK的Connector当中,因此其复用成本非常低。用户只要 insert 分区流表,自然就会按照定好的分区规则写到不同的 topic 里,能够做到和普通流表一样的使用,所以很容易推广复用,在任何层次或任何业务上均可使用分区流表来减少数据的吞吐。

此外,在运维工作上我们也会做很多工作,可以做到运行时的动态的分区修改。

产品实现

我们是如何实现的?下图左侧为产品图,在流表上会配一些分区规则,它是哪个topic, 其管理在源数据端可以管理到。

上图右侧为整体方案,参考了 Hive 的方案。因为离线数据的分区表是相对静态的,无需考虑其变化,而且批任务一般是调度执行,分区发生变厚,任务下一次再去读它能感知到即可。

但是在实时情况下则不同,任务都是常驻执行,需要感知分区的变化,并做出相应的调整,所以我们构建了一个 meta server, 它会与任务同步流表的分区规则,在SINK 侧,实时程序会和meta server实时同步分区规则,根据消息内容和分区规则选择相应的topic进行写入即可。同时写入程序也会往meta server上报血缘,保证后续在meta server上能很好的监控到哪些任务在写入数据,写入的状况如何,进而保证运维能够监控到每个任务的状态。

在 source 侧相对复杂一些,同样需要定时和meta server同步分区规则信息,任务启动时通过用户的查询条件请求meta server获取正确的分区,进行分区剪枝。我们将分区计算的这一次操作的主要逻辑放在server端,用以保证后续若分区剪枝能力出现一些问题或变化时,能够在 server 端自动去调整,直接把分区结果返回给source,而非改造引擎的代码,将改动多的逻辑中心化掉,减少后期维护的成本。

在改造过程中我们也发现了一些问题,如果流表上有watermark的配置,Flink SQL代码中分区规则会下推不下去,Flink SQL的RBO的Rule对这种情况支持有一些问题。就此我们对Flink SQL源代码也做了一些改造,添加了相应的Rule来支持这种情况下分区流表的分区下推。

为了感知分区的变化,source端也会和meta server不断的同步分区关系,在分区规则发生变化时,source端也会重新请求meta server获取最新的topic列表,读取最新的数据,保障读取数据的正确性,适配运行时动态地调整分区规则,降低整体的运维成本。同时我们也会上报读取血缘到meta server,方便运维管理,也能在meta server监控到所有消费端的状态。

如此一来,我们就完成了整个实时的分区流表建设工作时,用户在建设实时数仓时多了构建实时分区的手段,根据其业务做一些细致化的分区来保障整个数仓建模一致性的同时,也能保障性能。基本上不用过多的考虑其性能问题,只需要按照业务的需求和流量大小合理地设计分区即可。

分区流表技术建设-效果

主要的效果之一,即模型做到了统一。现在我们已经覆盖了所有的音乐业务线,任务的稳定性问题也得到了彻底的解决。

因为以前流表分发粒度很粗,使用起来成本很高,后期维护起来也很难,但是上了分区流表以后,我们可以做一些很细致化的分区。根据业务的需求经验与数仓去合作做很细致化的分区,基于此一些任务减少了很多流量的读取。

历史任务完成分区改造迁移以后,其实我们节省能有 700W+的成本。

我们的 Kafka 的流量也大大降低了,基本上2022年全年几乎没有采购一台Kafka,本身现在 Kafka 的压力也非常可控,总体而言达到了非常好的效果。

数据任务治理实践

降本提效

2022年的经济形势也不是特别好,所以各大公司都在提降本提效,以下讲述我们做任务治理上的一些实践经验,如何做降本提效。接下来主要讲整体提效任务治理的思路。

我们作为一个业务方的数据平台,希望任务用得越合理越好。总体而言,整体工作,分为以下四个部分。

1.摸清现状,高效治理。我们先梳理下线索,有的放矢,而非眉毛胡子一把抓,以最小的人力获取最好的治理效果。

2.清理无用任务。我们拿到现状的时候,希望能够用很少的人力,以高 ROI 的方式,做一些无用任务的清理。有些任务本身消耗了很多资源,但是产出的数据相关业务方已经不再使用了,就果断加以清理。

3.优化资源配置。有些任务资源消耗很大,但是数据量很小,资源量和数据量不是很匹配,我们想办法优化其资源的配置。

4.自动化,可持续。上述三部分,均属于运动式的治理工作。长远来看,实为内耗。我们在做完这份工作以后,实则希望能够可持续、自动化地将治理的工作做到平台化、常态化,此乃降本增效的终极目标,而非每年都做运动式的治理。

摸清现状

摸清现状后,主要为两部分:存储成本和计算成本。

存储成本:我们有一个专门的平台,能够统计出所有数据的成本,即每个数据需要花费多少。

计算成本:这部分投入了更多时间、精力,我们会在每个任务运行完以后,将成本直接反馈到用户那里,使其对成本有感知。另外,摸清每个任务每次执行的消耗,后续即可挑一些高消耗的任务去做相应优化和治理。

拥有了这些成本数据,我们会根据其任务的归属做一些部门的聚合统计,能够让部门感知到数据的花费情况,此后也可以进行反推,限制每个部门整体的资源使用。

再看下数据任务治理实践中的任务下线。

任务下线

血缘:梳理可下线的任务,即根据血缘发现无用表、无用任务。

运维积极性:根据运维的积极性,比如一个任务经常报警,没人管。

僵尸任务:对离职、转岗或是长期不更新的一些任务,加以确认。

任务血缘:根据活动的数据,以及下游访问记录做一些任务血缘,判断哪些任务无用。

基本上,我们是基于上述一套流程来加以操作的。 我们部门主要提供的是数据支持的工作,主要数仓在做下线的工作。整体成本上,我们上半年节省了 2000 万左右, 占20% 左右,还是较为可观的。

优化

实时计算部分做技术配置优化,其实主要是在优化上。

1. 技术升级。我们将分区流表全部覆盖,做了全面推广及升级改造,其工作量非常之大。

2. 配置优化。对资源梳理后,即可获取每个任务的消耗资源,挑出来做一些资源消耗比较大的任务进行治理优化,资源调整,或者看下其流量是否配置得合理。我们发现很多用户对 Flink 任务的配置比较随意,非常不合理。这部分也节省了不少成本。

3. 资源优化。针对一些任务做不同的定制化的资源配比,比如一些流量较小,IO操作比较多的任务,我们会将这类任务迁移到超售比例比较高的集群的Label上,减少整体资源的消耗。YARN的资源调度管理目前还非常的粗,该部分未来会基于K8S做一些资源管理精细化的工作,保障整体的资源消耗。

做完上述治理以后,实际的资源消耗从 80% 多下降到 50% 多,下降了30%,优化效果显著。

可持续

可持续是我们正在做的事情,希望告别大扫除的运动式的治理,能够做到数据治理的常态化、平台化。

基于此我们构建了一个系统,用数据治理数据。我们平台叫做公孙策平台,会收集任务、数仓、资源等元数据,然后通过元数据配置一些规则,能够通过规则发现一些不合理的任务,以及一些已经需要下线的,或是资源/需求不合理的任务。发现这些任务以后,结下来就是需要推动用户主动去优化改造。

如何推动用户主动改造?我们利用配置的规则对所有任务的质量进行打分,云音乐的技术中心有一个质量分机制,我们和集团统一质量分做整合,公示数据,去推动用户。质量分每一个平台老大都非常关注,会在全公司进行排名,所以只通过质量分的推动,用户去主动地做治理改造,能够完成一个闭环。

整体的工作效果,我们分为三个阶段:

开发前,根据任务配置和行为加以分析,做一些配置的推荐,上线的流程卡点,以保证配置的合理性。

运行中:做一些质量的扫描,任务的上下线。哪些任务需要下线,哪些任务配置不合理,对于不合理的任务,推进开发进行上下线操作或者配置优化。

下线后:我们会收集治理效果,对效果做一些排行榜,形成激励机制,以保证用户能够把任务治理作为一个常态化的工作。

未来规划

关于开发门槛问题、数据链路追踪、存储选择优化、开发经验沉淀等问题

结合上述问题,我们希望能够围绕以下四点 做一个新的业务化的低代码的开发工具来解决,整体建设思路如下:

1.低代码:我们希望通过能够通过一些低代码的方式来降低 Flink 的开发门槛。

2.端到端:从数据处理到下游服务,做统一的整合,希望把一些业务上封装的服务作为存储,屏蔽底层细节,回收服务的服务数据,闭环数据价值。

3.批流一体。与低代码的整合,其实今年也有很多公司在做,我们希望能够通过自定义的一套 DSL 生成 Flink 的实时流代码和 spark 的离线代码,能做到批流一体一套业务业务逻辑表达,同时生成实时任务和离线任务,统一管理调度,整合lambda架构,降低整体开发成本。

4.场景化。我们希望在业务场景的基础上,去打造达到符合上述三点的场景,解决用户的共性问题。

以下为未来整体的架构规划:

1.以数据模型为中心。

2.以配置化的方式作为主要开发手段。

3.上下游服务深度整合。

4.批流一体任务统一管理调度。

5.希望通过场景化解决我们 80% 通用的常见场景问题。

当然 80% 是拍拍脑袋随便估的,28原则, 我们希望通过这个平台,解决一些场景的共性问题。这个东西它还依赖于数仓本身的建模的完善,如果建模做得不好,其实我们很难做到低代码,因为业务逻辑很复杂,低代码就不太可能去实现。但是如果 DWD 层做得很完善,用户就可以专注在业务场景的逻辑开发上,非常高效地完成业务开发,提升开发效率,降低开发门槛。