首先,从四个方面介绍云音乐的实时数仓架构:云音乐的实时场景、实时数仓架构、技术架构以及技术选型。
目前云音乐的实时场景主要分为以下三个部分,如图:
该场景主要是配合算法团队构建实时特征。实时数据作为推荐、搜索社区的数据输入,主要用于歌曲推荐、搜索热度、场景排序、新歌热歌推广等场景。其次,针对首页和搜索流量分发场景,为了提升流量的转化效率,可以对不同用户进行个性化的场景排序,如:偏向于听歌的用户会把推荐歌曲模块放在上面、偏向于听有声书的则将播客模块置顶等,从而提升用户的流量转化效率。
主要针对于资源投放效果和 AB 实时效果。实时和准实时的反馈可以尽早提醒业务运营或者业务中后台了解投放效果如何等。如果效果未达到预期,业务同学就有更充足的时间来分析其中的原因并进行调整。还有在针对机械刷歌和刷红心点赞的行为时,实时风控可以发现异常用户,并采取账号封禁或无效流量分流等策略,防止这类脏数据进入到业务数据,影响下游数据分析的准确性。
即实时大屏。通过实时大屏向管理层提供当天的流量转化和业务增长等最直观的数据表现。并且实时的用户分析、歌曲表现分析、活动分析、场景分析等特征数据作为业务输入,可以让运营有数据可依,从而更有效的进行精准推歌、活动运营,防止用户流失。
针对以上这些场景,云音乐构建了一套基于维度建模的实时数仓架构模型,如图:
接收来自日志服务器和业务服务器的原始流量数据以及业务数据库的 binlog 日志,作为实时数仓中间层的输入。
实时数仓中间层根据日志采集层的数据进行清洗和解析形成流量中间层和业务明细中间层。目前流量中间层只对基于埋点的公共属性进行解析,而对于业务的个性化场景不会进行处理。原因在于实时数仓包含了主站所有的流量数据,业务直接使用的成本较高。即使流量中间层会基于各垂类场景进行分区分流,但例如会员这类横向业务场景仍然需要读取所有的垂类业务来获取全量数据。为了解决此类问题,云音乐构建了相应的业务明细中间层,进行个性化业务参数解析和流量聚焦。因此在基础流量中间层的基础上又构建了相关场景的业务明细中间层。比如:搜索中间层、直播中间层以及社区中间层等。
在离线数仓架构中,为了提升数据的复用度一般会构建 DWS 轻度汇总层,但在实时场景中因为流式计算的特性,轻度汇总层存在的意义就有待商榷了。
不过近些年来由于 ClickHouse 等 OLAP 引擎的兴起,部分实时场景为了高扩展性,更偏向于基于明细数据进行查询,而非直接查询计算结果。在这类场景下,通过 OLAP 引擎构建 DWS 表,则可以大大降低 OLAP 的存储量和前台业务的计算成本。应用层的数据基本上都是基于中间层聚合生成,应用服务可以直接读取这些数据,主要用于相关数据产品算法模型的输入、业务产品展示等。
基于构建的 ADS 表进行相关业务分析决策,并应用于推荐算法、实时风控和精准推歌等场景。
云音乐的实时数仓技术架构基本上和目前大多数大厂的实时数仓技术架构类似,如图:
在离线场景下,基于 Spark + Hive 架构来实现。在实时场景中从 ODS 层到 CDM 层是通过 Flink + Kafka 的模式实现。从 CDM 层到 ADS 层,准实时场景是 Impala + Kudu 或者 ClickHouse 存储引擎,来支持 OLAP 即席查询。
云音乐的实时数仓技术选型也是经过了场景适配、成本评估等一系列调研后才得出结论,主要有三条链路。这三条链路主要是基于业务对于时效性保障、稳定性、可扩展性和普适性的取舍。
针对于大屏类和业务算法类需求,这部分场景对于更新频率、延时性要求极高。比如实时大屏要求看到秒级的数据变动、算法团队要求看到 5 分钟级别的用户特征更新。这类场景基本无法通过准实时链路来实现。因此这类场景的需求会通过 Flink 进行增量或全量计算,将结果数据写入 redis、HBase 或 MySQL 等可以直接查询的业务数据源中。这类任务基本上都是优先级较高的实时任务,需要重点保障。因此还需要进行全链路多备份,防止由于集群问题对大屏数据或者业务数据产生影响。这类任务的消耗和异常的修复成本都是非常高的,所以需要尽量保证其高可用。
这类主要是 5min 以上的时效性需求,可以充分利用 OLAP 分析工具,通过 Flink 将明细数据写入到 ClickHouse、Kudu 或者 Hive 中。然后通过 ClickHouse、Impala 等 OLAP 引擎进行查询或者分钟级别的调度,实现数据计算。因为这套方案操作的是明细数据,因此较前一种场景的可扩展性会更强,但是由于需要通过 OLAP 引擎进行即席查询,这类产品一般需要进行新的技术栈探索,普适性比较弱,学习成本较高,分析师通常不会使用这种模式来进行数据分析。
这类场景对于时效性的要求会更低,一般是 2 小时以上的数据延迟。通过 DS 采集和 Hive+Spark 生成一套准实时的、小时级别的数据,通过批处理链路进行调度计算。该链路无其他特殊的技术栈,可以直接提供给分析师来使用,适配的分析场景也更多。并且可以将复杂的解析逻辑和清洗过程放到小时任务中,T+1 的离线任务基于小时产出数据合并获取,可以大大提升离线基础表的产出效率。
在构建实时数仓时,一般都会有相对应的离线数仓。主要用于历史数据的回刷以及更为复杂的场景支持,这样难免就会存在两条链路进行计算,流链路和批链路,这也就是我们常说的 Lambda 架构。
当天的数据会通过流计算任务处理生成实时表提供数据接口给数据应用。历史数据会通过批计算,生成离线表提供给数据服务。
实时和离线两套方案独立存在,这时可能会造成数据模型不一致的情况。云音乐大部分场景通过字段名称统一和表名统一来保证实时模型和离线模型的一致性。但是针对分区模型的话,实时场景比较难处理。
例如实时数仓中用户行为日志产生的信息通过 DS 采集写入到 Kafka 中,作为实时数仓的 ODS 层输入,并且还有一份会写入到 HDFS 中作为离线数仓的 ODS 层输入。通过实时数仓还有离线数仓的中间层解析操作生成流量中间层。
但是因为流量中间层的数据量过大,不可避免的需要对流量进行分区分流操作。在离线侧进行分区是比较容易的,可以通过 Hive 进行目录级别的分区,然后将数据文件动态的写入到分区目录中,通过 Hive 的元数据进行管理。而在实时侧则只能通过 Kafka topic 进行物理分区,通过手工维护映射规则。这样会存在一些问题:第一,通过文档维护 topic,维护成本非常高;第二,用户同样需要知道他们需要的信息是来自哪个 topic,用户的开发成本和使用成本也非常高。
因此针对这个实时场景分区难维护的问题,网易云音乐搭建了一套 Kafka 的分区流表。具体模型如下图:
分区流表内部维护了一整套的分区字段到 Kafka 物理 topic 的映射规则。写入侧和读取侧是无需要关注这套规则的,只需要按照离线表的动态写入规则来进行开发即可,分区流表会基于这些规则进行相关的转换和映射。
下面介绍下云音乐分区流表在具体实现上做的优化:
(1)分区映射管理。平台提供了分区流表的分区管理机制,用户在建表过程中可以指定实时表为分区流表,并将分区值和物理 Topic 进行绑定。平台元数据管理中心则基于用户配置信息将分区流表构建成一个内存表,以便能够快速获取符合条件的物理 Topic。
(2)分区读写路由。为了支持分区路由,云音乐重写了 table source 和 table sink。在写入侧基于分区字段值自动匹配其对应的物理 Kafka topic。在读取侧则会根据 where 条件中的分区条件字段对分区进行剪枝,并将剪枝条件推向元数据管理中心,元数据管理中心通过查询内存表,将符合条件的 Topic 返回给读取侧以达到读取部分分区的效果。
(3)边界情况优化。为了提升计算效率和任务稳定性,云音乐进行了一系列的优化操作。在写入和读取时,可以配置默认的 Topic,对于那些没有命中配置分区的记录将会被写入到默认 Topic 中,这样可以降低分区创建的复杂度,并可以对默认分区进行监控,按需进行单独分区或者动态分区扩展。分区流表还支持多个分区字段指向同一个Topic,以防止 Topic 数的急剧扩张,虽然这样可能会导致下游实际读取的数据量会比单分区单 Topic 模式有所增长,但是平台通过优化分区字段匹配机制降低下游读取时的反序列化成本,极大提升了下游的计算效率。即便有这种优化机制,分区流表创建者仍需要考虑 Topic 数和下游使用便利性的平衡。
(4)动态分区架构。以上方案均是针对静态分区机制,即在分区流表创建之后基本不会发生分区的新增和变化,或者只能通过上下游通知重启来感知分区变化。静态分区机制基本能满足 90% 的实时场景。然而可能存在部分业务场景变化较快,可能频繁进行分区扩增、更改,这样会造成维护成本升高。基于这方面的考虑,云音乐提出了动态分区的思想和技术实现。以下是动态分区技术架构:
整个架构的前半部分和静态分区是大致相同的,主要区别在于应对规则变更的实现流程。平台通过引入 ZooKeeper 进行监听分区流表的变更,来实现所有依赖的统一变更。当存在分区新增/修改时,Zookeeper 将会触发分区流表上下游进行状态快照,保存修改前的分区信息、消费时间戳信息。然后针对之后的所有记录,写入端对比该记录时间戳和修改分区时间戳,如果在修改分区之后,将写入新的分区中;读取端则是会按照修改时间戳拉取新增 Topic,基于事件时间和分区时间判断消费数据。并且为了保证规则的一致性,平台引入了两阶段的通知机制,确保规则同时生效和同时回滚,实现链路的幂等性。目前该方案还在测试和验证阶段,功能暂未上线。
云音乐分区流表的实现基本确保了实时模型和离线模型的一致性,但是由于计算链路的差异还是不能保证实时和离线计算的一致性。基于该问题,云音乐也进行深入的探究和思考。
目前,针对于计算逻辑一致性的解决方案大致有三类:KAppa 架构、DataPhin 平台和低代码平台。
(1)Kappa 架构:目前 Kappa 架构不太适用于云音乐的场景。大数据量的回刷、Flink 吞吐量的受限以及差业务异化的无法处理,很难满足目前云音乐的场景。
(2)DataPhin 平台:强依赖 Flink 技术栈的开发。Filnk 的维护成本和学习成本较高,对于刚接触流计算的同学而言还是有一定学习成本的。并且很难保证批任务计算资源的充足。
(3)低代码平台:目前云音乐采用的技术方案。主要通过可视化、组件化和配置化将数据开发进行退化,来依托于平台。从而极大的释放生产力:数据开发可以更专注于数据中间层和数据资产的建设、数据分析师可以更专注于指标的开发和看板的搭建、数据运营和产品可以在不依托于数据开发的前提下来进行需求任务创建。
目前云音乐的实时、离线数仓主要存在于以下四类问题:
(1)数据模型:在模型设计阶段标准、规范很难统一,并且在已有标准的情况下是很难按标准实施。并且即使数仓同学维护了较全面的白皮书文档,但在向分析师和运营的同学推广时也遇到了比较大的阻力,导致数仓构建的比较好用的宽表不能为业务输出相应的能力。
(2)计算模型:由于业务提供的指标口径是在不同业务场景下定义的,导致指标口径不一致且复杂混乱。
(3)实时和离线开发:由于技术栈和支撑场景的差异化,导致开发成本、运维成本较高,并且数据一致性较难保证。
(4)资产评估:目前实时数仓还没有一套体系化的方案来定位问题,问题的排查比较困难、解决链路较长。并且,由于数据资产的不完备,也很难量化实时模型的价值。
为了解决上述的问题,实现流批场景的计算一致性,云音乐数据平台构建了 FastX 平台。下面和大家分享下 FastX 架构实现。首先我会对 FastX 的各层职能进行简单介绍,然后再分别从模型化、组件化、配置化、服务化血缘化介绍 FastX 的具体架构思想。
模型层:数据模型管理、指标管理。
配置层:用户可以配置对应数据模型的输入、输出以及相关的计算模型。
执行层:将任务转换成 Spark 或者 Flink 可执行的调度。
运维层:主要职责监控、报警。
服务层:外部应用通过 API 访问数据模型,提供数据服务。
以上就是 FastX 的架构各层的主要职能,下面和大家分享下 FastX 抽象后的各个部分主要功能:
FastX 是基于模型化进行开发、管理的。数据模型是 Fastx 的唯一出口。目前
FastX 数据模型包含三类:
视图模型:实时流表和离线天表的映射,作为写到 FastX 任务的输入。
AB 模型:优化 AB 场景的数据开发和指标管理。
关系模型:维护了实时和离线的开发任务,指向实时场景和离线场景的物理存储。
FastX 将数据模型内部定义了一整套的组件化开发模型,将任务抽象为输入、输出和计算模型,通过拖拉拽的形式实现任务的开发。通过组件化的自适应能力,用户可以不用关心是否需要流场景或者批场景、数据来自哪里或者去哪里,这些 FastX 都会自动识别。
配置化主要是在开发的易用性方面的优化处理,例如:
针对 Binlog 实时同步场景,通过可视化的配置,实现 Binlog 的解析和 Topic 的订阅,极大的减少了开发成本和使用成本;
针对于 ClickHouse sink 场景,由于 ck 的 sql 语法与其他 RDBMS 语法差异较大,并且使用上也存在很多不同。为了减轻学习成本,FastX 在简化 ck 的使用上做出了很大的努力。例如,提供了一键式建表工具,用户只需要根据实际场景配置索引和分区就可以一键生成建表 sql;针对离线和实时数据的回刷场景提供了一键式数据切换;针对分区表和非分区表自适应选择底层的实现逻辑,防止线上数据跌 0 的情况;针对于写入端,FastX 实现了自定义 shard 写入模型,通过 Flink 进行 Hash 分流将数据直接写入 ck 的本地表,减轻了 ck 的代理压力。
针对于 AB 开发场景,与 AB 实验平台进行打通,支持 AB 实验平台的指标一键式导入,并且可以进行批量口径管理,降低 AB 实验开发复杂度。将 AB 任务转化为指标口径录入,使得实时指标开发无需数据开发介入,产品、分析师等都可以通过指标录入完成实时 AB 任务开发。
由于 FastX 可以作为数据源进行数据模型的输入的特性,因此可以根据外部的调用情况来评估数据模型的价值。并且数据模型还可以通过任务和服务调用情况构建数据模型和应用的血缘关系图,还作为模型治理和成本优化的后期依据。
云音乐实时数仓之后将围绕以下内容进行继续探索:
利用 FastX 的数据模型和血缘关系进行实时资产的统一管控;
和 FastX 共建多链路保障机制。
利用血缘关系实现实时资产的评估机制。
扩张更多复杂的计算模型。
增加端到端解决方案,屏蔽实现细节。
构建复杂的流批场景。
实时湖仓建设。
A1:指标管理和计算任务是解耦的,指标需求可以先提出,然后进行开发。最后在指标录入的阶段将指标口径进行录入,不需要依赖指标口径的录入来进行需求管理。
A2:目前通过多集群的配置。例如在大促类需求时,会针对 Flink 集群和底层存储集群做多集群的配置。如果线上集群出现问题,可以通过一键式切换到其他数据源,保证数据的可用性。切换完成后会对故障集群进行问题排查、修复,完成后再将集群切回主链路。
A3:binlog 主要适用于关系型数据库存储的业务数据,其他类似于流量日志会通过 DS 采集日志服务器中的数据,然后直接写入到 Kafka 中。
A4:实时计算增加小时级别的计算窗口和离线数据保持一致。计算口径上实时任务和离线任务的口径是保持一致的,例如通过日志时间来进行计算。
A5:实时计算的处理思路基本和离线是一致的,会通过多读取一段时间的数据来保证数据的完整性。