导读:腾讯音乐内容库数据平台旨在为应用层提供库存盘点、分群画像、指标分析、标签圈选等内容分析服务,高效为业务赋能。目前,内容库数据平台的数据架构已经从 1.0 演进到了 4.0 ,经历了分析引擎从 ClickHouse 到 Apache Doris 的替换、经历了数据架构语义层的初步引入到深度应用,有效提高了数据时效性、降低了运维成本、解决了数据管理割裂等问题,收益显著。本文将为大家分享腾讯音乐内容库数据平台的数据架构演进历程与实践思考,希望所有读者从文章中有所启发。
作者:腾讯音乐内容库数据平台 张俊、代凯
腾讯音乐娱乐集团(简称“腾讯音乐娱乐”)是中国在线音乐娱乐服务开拓者,提供在线音乐和以音乐为核心的社交娱乐两大服务。腾讯音乐娱乐在中国有着广泛的用户基础,拥有目前国内市场知名的四大移动音乐产品:QQ音乐、酷狗音乐、酷我音乐和全民K歌,总月活用户数超过8亿。
腾讯音乐娱乐拥有海量的内容曲库,包括录制音乐、现场音乐、音频和视频等多种形式。通过技术和数据的赋能,腾讯音乐娱乐持续创新产品,为用户带来更好的产品体验,提高用户参与度,也为音乐人和合作伙伴在音乐的制作、发行和销售方面提供更大的支持。
在业务运营过程中我们需要对包括歌曲、词曲、专辑、艺人在内的内容对象进行全方位分析,高效为业务赋能,内容库数据平台旨在集成各数据源的数据,整合形成内容数据资产(以指标和标签体系为载体),为应用层提供库存盘点、分群画像、指标分析、标签圈选等内容分析服务。
图片
TDW 是腾讯最大的离线数据处理平台,公司内大多数业务的产品报表、运营分析、数据挖掘等的存储和计算都是在TDW中进行,内容库数据平台的数据加工链路同样是在腾讯数据仓库 TDW 上构建的。截止目前,内容库数据平台的数据架构已经从 1.0 演进到了 4.0 ,经历了分析引擎从 ClickHouse 到 Apache Doris 的替换、经历了数据架构语义层的初步引入到深度应用,有效提高了数据时效性、降低了运维成本、解决了数据管理割裂等问题,收益显著。接下来将为大家分享腾讯音乐内容库数据平台的数据架构演进历程与实践思考。
图片
如图所示为数据架构 1.0 架构图,分为数仓层、加速层、应用层三部分,数据架构 1.0 是一个相对主流的架构,简单介绍一下各层的作用及工作原理:
存在的问题:
除此之外,ClickHouse 由国外开源,交流具有一定的语言学习成本,遇到问题无法准确反馈、无法快速获得解决,与社区沟通上的阻塞也是促进我们进行架构升级的因素之一。
图片
基于架构 1.0 存在的问题和 ClickHouse 的局限性,我们尝试对架构进行优化升级,将分析引擎 ClickHouse 切换为 Doris,Doris 具有以下的优势:
Apache Doris 的优势:
同时我们也利用 Doris 的特性,解决了架构 1.0 中较为突出的问题。
架构 2.0 存在的问题:
针对指标和标签定义口径不统一,数据使用和管理难度较高的问题,我们继续对架构进行升级。数据架构 3.0 主要的变化是引入了专门的语义层,语义层的主要作用是将技术语言转换为业务部门更容易理解的概念,目的是将标签 (tag)与指标(metric)变为“一等公民”,作为数据定义与管理的基本对象。
图片
引入语义层的优势有:
存在的问题:
从架构图可知,标签和指标等数据均处于下游位置,虽然标签与指标在语义层被显式定义,但仍然无法影响上游链路,数仓层有自己的语义逻辑,加速层有自己的导入配置,这样就造成了数据管理机制的割裂。
在数据架构 3.0 的基础上,我们对语义层进行更深层次的应用,在数据架构 4.0 中,我们将语义层变为架构的中枢节点,目标是对所有的指标和标签统一定义,从计算-加速-查询实现中心化、标准化管理,解决数据管理机制割裂的问题。
图片
语义层作为架构中枢节点所带来的变化:
架构优势:
存在的问题:
因为当前架构是对单个标签和指标进行了定义,因此如何在查询计算时自动生成一个准确有效的 SQL 语句是非常有难度的。如果你有相关的经验,期待有机会可以一起探索交流。
从上文已知,为更好地实现业务需求,数据架构演进到 4.0 版本,其中 Apache Doris 作为分析加速场景的解决方案在整个系统中发挥着重要的作用。接下来将从场景需求、数据导入、查询优化以及成本优化四个方面出发,分享基于 Doris 的读写优化经验,希望给读者带来一些参考。
图片
目前我们有 800+ 标签, 1300+ 指标,对应 TDW 中有 80 + Source 表,单个标签、指标的最大基数达到了 2 亿+。我们希望将这些数据从 TDW 加速到 Doris 中完成标签画像和指标的分析。从业务的角度,需要满足以下要求:
图片
为了减轻 Doris 写入压力,我们考虑在数据写入 Doris 之前,尽量将数据生成宽表,再写入到 Doris 中。针对宽表的生成,我们有两个实现思路:第一个是在 TDW 数仓中生成宽表;第二个是 Flink 中生成宽表。我们对这两个实现思路进行了实践对比,最终决定选择第二个实现思路,原因如下:
在 TDW 中生成宽表,虽然链路简单,但是弊端也比较明显。
而在 Flink 中生成宽表,链路简单、成本低也容易实现,主要流程是:首先用 Spark 将相关 Source 表最新数据离线导入到 Kafka 中, 接着使用 Flink 来消费 Kafka,并通过主键 ID 构建出一张大宽表,最后将大宽表导入到 Doris 中。如下图所示,来自数仓 N 个表中 ID=1 的 5 条数据,经过 Flink 处理以后,只有一条 ID=1 的数据写入 Doris 中,大大减少 Doris 写入压力。
图片
通过以上导入优化方案,极大地降低了存储成本, TDW 无需维护两份冗余的数据,Kafka 也只需保存最新待导入的数据。同时该方案整体实时性更好且可控,并且大宽表聚合在 Flink 中执行,可灵活加入各种 ETL 逻辑,离线和实时可对多个开发逻辑进行复用,灵活度较高。
目前我们生产环境所使用的版本为 Apache Doris 1.1.3,我们对其所支持的 Unique 主键模型、Aggregate 聚合模型和 Duplicate 明细模型进行了对比 ,相较于 Unique 模型和 Duplicate 模型,Aggregate 聚合模型满足我们部分列更新的场景需求:
Aggregate 聚合模型可以支持多种预聚合模式,可以通过REPLACE_IF_NOT_NULL
的方式实现部分列更新。数据写入过程中,Doris 会将多次写入的数据进行聚合,最终用户查询时,返回一份聚合后的完整且正确的数据。
另外两种数据模型适用的场景,这里也进行简单的介绍:
确定数据模型之后,我们在建表时如何对列进行命名呢?可以直接使用指标或者是标签的名称吗?
在使用场景中通常会有以下几个需求:
Doris 1.1.3 是不支持对列名进行修改的,如果直接使用指标/标签名称作为列名,则无法满足上述标签或指标更名的需求。而对于上下架标签的需求,如果直接以 drop/add column 的方式实现,则会涉及数据文件的更改,该操作耗时耗力,甚至会影响线上查询的性能。
那么,有没有更轻量级的方式来满足需求呢?接下来将为大家分享相关解决方案及收益:****
song_name
的 ID 为 4,在 Doris 中存储命名为 a4,用户使用更具有业务含义song_name
进行查询。在查询 Doris 前,我们会在查询层将 SQL 改写成具体的列名 a4。这样名称的修改只是修改其元数据,底层 Doris 的表结构可以保持不变。值得关注的是,在社区近期发布的 1.2.0 版本中,增加了 Light Schema Change 功能, 对于增减列的操作不需要修改数据文件,只需要修改 FE 中的元数据,从而可以实现毫秒级的 Schame Change 操作。同时开启 Light Schema Change 功能的数据表也可以支持列名的修改,这与我们的需求十分匹配,后续我们也会及时升级到最新版本。
接着我们在数据写入方面也进行了调整优化,这里几点小经验与大家分享:
max_XXXX_compaction_thread
max_cumulative_compaction_num_singleton_deltas
优化背景:在写入时发现某一个 BE负载会远远高于其他的 BE,甚至出现 OOM。结合源码发现:作业启动后会获取一次 BE 地址列表,从中随机选出一个 BE 作为 Coordinator 协调者,该节点主要负责接收数据、并分发到其他的 BE 节点,除非作业异常报错,否则该节点不会发生切换。
对于少量 Flink 作业大数据场景会导致选中的 BE 节点负载较高,因此我们尝试对 BE 提交逻辑进行优化,设置每 1 小时缓存一次 BE 列表,每写入一个批次都随机从 BE 缓存列表中获取一个进行提交,这样负载均衡的粒度就从 job 级别细化到每次提交的批次,使得 BE 间负载更加的均衡,这部分实现我们已经贡献到社区,欢迎大家一起使用并反馈。
图片
通过以上数据导入的优化措施,使得整体导入链路更加稳定,每日离线导入时长下降了 75% ,数据版本累积情况也有所改善,其中 cumu compaction 的合并分数更是从 600+直降到 100 左右,优化效果十分明显。
图片
目前我们的场景指标数据是以分区表的形式存储在 Doris 中, ES 保留一份全量的标签数据。在我们的使用场景中,标签圈选的使用率很高,大约有 60% 的使用场景中用到了标签圈选,在标签圈选场景中,通常需要满足以下几个要求:
经过调研,我们最终采用了 Doris on ES 的解决方案来实现以上要求,将 Doris 的分布式查询规划能力和 ES 的全文检索能力相结合。Doris on ES 主要查询模式如下所示:
SELECT tag, agg(metric)
FROM Doris
WHERE id in (select id from Es where tagFilter)
GROUP BY tag
在 ES 中圈选查询出的 ID 数据,以子查询方式在 Doris 中进行指标分析。
我们在实践中发现,查询时长跟圈选的群体大小相关。如果从 ES 中圈选的群体规模超过 100 万时,查询时长会达到 60 秒,圈选群体再次增大甚至会出现超时报错。经排查分析,主要的耗时包括两方面:
图片
针对这两点,我们进行了以下优化:
图片
es_optimize
,以开启优化开关;通过以上优化措施,百万分群圈选洞察查询时间从最初的 60 秒缩短到 3.7 秒,性能显著提升!
经过与社区沟通交流,Apache Doris 从 2.0.0 版本开始,将支持倒排索引。可进行文本类型的全文检索;支持中文、英文分词;支持文本、数值日期类型的等值和范围过滤;倒排索引对数组类型也提供了支持,多个过滤条件可以任意进行 AND OR NOT 逻辑组合。由于高性能的向量化实现和面向 AP 数据库的精简优化,Doris 的倒排索引相较于 ES 会有 3~5 倍性价比提升,即将在 2 月底发布的 2.0 preview 版本中可用于功能评估和性能测试,相信在这个场景使用后会有进一步的性能提升。
在当前大环境下,降本提效成为了企业的热门话题,如何在保证服务质量的同时降低成本开销,是我们一直在思考的问题。在我们的场景中,成本优化主要得益于 Doris 自身优秀的能力,这里为大家分享两点:
1、冷热数据进行精细化管理。
2、降低数据链路成本。
Doris 架构非常简单,只有FE 和 BE 两类进程,不依赖其他组件,并通过一致性协议来保证服务的高可用和数据的高可靠,自动故障修复,运维起来比较容易;
通过以上的方式,使得存储成本降低 42%,开发与时间成本降低了 40% ,成功实现降本提效,后续我们将继续探索!
未来我们还将继续进行迭代和优化,我们计划在以下几个方向进行探索:
最后,感谢 Apache Doris 社区和 SelectDB 的同学,感谢其快速响应和积极支持,未来我们也会持续将相关成果贡献到社区,希望 Apache Doris 飞速发展,越来越好!