导读:腾讯音乐内容库数据平台旨在为应用层提供库存盘点、分群画像、指标剖析、标签圈选等内容分析服务,高效为业务赋能。目前,内容库数据平台的数据架构曾经从 1.0 演进到了 4.0 ,经验了剖析引擎从 ClickHouse 到 Apache Doris 的替换、经验了数据架构语义层的初步引入到深度利用,无效进步了数据时效性、升高了运维老本、解决了数据管理割裂等问题,收益显著。本文将为大家分享腾讯音乐内容库数据平台的数据架构演进历程与实际思考,心愿所有读者从文章中有所启发。
作者:腾讯音乐内容库数据平台 张俊、代凯
腾讯音乐娱乐团体(简称“腾讯音乐娱乐”)是中国在线音乐娱乐服务开拓者,提供在线音乐和以音乐为外围的社交娱乐两大服务。腾讯音乐娱乐在中国有着宽泛的用户根底,领有目前国内市场出名的四大挪动音乐产品:QQ音乐、酷狗音乐、酷我音乐和全民K歌,总月活用户数超过8亿。
业务需要
腾讯音乐娱乐领有海量的内容曲库,包含录制音乐、现场音乐、音频和视频等多种形式。通过技术和数据的赋能,腾讯音乐娱乐继续翻新产品,为用户带来更好的产品体验,进步用户参与度,也为音乐人和合作伙伴在音乐的制作、发行和销售方面提供更大的反对。
在业务经营过程中咱们须要对包含歌曲、词曲、专辑、艺人在内的内容对象进行全方位剖析,高效为业务赋能,内容库数据平台旨在集成各数据源的数据,整合造成内容数据资产(以指标和标签体系为载体),为应用层提供库存盘点、分群画像、指标剖析、标签圈选等内容分析服务。
数据架构演进
TDW 是腾讯最大的离线数据处理平台,公司内大多数业务的产品报表、经营剖析、数据挖掘等的存储和计算都是在TDW中进行,内容库数据平台的数据加工链路同样是在腾讯数据仓库 TDW 上构建的。截止目前,内容库数据平台的数据架构曾经从 1.0 演进到了 4.0 ,经验了剖析引擎从 ClickHouse 到 Apache Doris 的替换、经验了数据架构语义层的初步引入到深度利用,无效进步了数据时效性、升高了运维老本、解决了数据管理割裂等问题,收益显著。接下来将为大家分享腾讯音乐内容库数据平台的数据架构演进历程与实际思考。
数据架构 1.0
如图所示为数据架构 1.0 架构图,分为数仓层、减速层、应用层三局部,数据架构 1.0 是一个绝对支流的架构,简略介绍一下各层的作用及工作原理:
- 数仓层:通过 ODS-DWD-DWS 三层将数据整合为不同主题的标签和指标体系, DWM 集市层围绕内容对象构建大宽表,从不同主题域 DWS 表中抽取字段。
- 减速层:在数仓中构建的大宽表导入到减速层中,Clickhouse 作为剖析引擎,Elasticsearch 作为搜寻/圈选引擎。
- 应用层:依据场景创立 DataSet,作为逻辑视图从大宽表选取所需的标签与指标,同时能够二次定义衍生的标签与指标。
存在的问题:
- 数仓层:不反对局部列更新,当上游任一起源表产生提早,均会造成大宽表提早,进而导致数据时效性降落。
- 减速层:不同的标签跟指标个性不同、更新频率也各不相同。因为 ClickHouse 目前更善于解决宽表场景,无区别将所有数据导入大宽表生成天的分区将造成存储资源的节约,保护老本也将随之升高。
- 应用层:ClickHouse 采纳的是计算和存储节点强耦合的架构,架构简单,组件依赖重大,牵一发而动全身,容易呈现集群稳定性问题,对于咱们来说,同时保护 ClickHouse 和 Elasticsearch 两套引擎的连贯与查问,老本和难度都比拟高。
除此之外,ClickHouse 由国外开源,交换具备肯定的语言学习老本,遇到问题无奈精确反馈、无奈疾速取得解决,与社区沟通上的阻塞也是促成咱们进行架构降级的因素之一。
数据架构 2.0
基于架构 1.0 存在的问题和 ClickHouse 的局限性,咱们尝试对架构进行优化降级,将剖析引擎 ClickHouse 切换为 Doris,Doris 具备以下的劣势:
Apache Doris 的劣势:
- Doris 架构极繁难用,部署只需两个过程,不依赖其余零碎,运维简略;兼容 MySQL 协定,并且应用规范 SQL。
- 反对丰盛的数据模型,可满足多种数据更新形式,反对局部列更新。
- 反对对 Hive、Iceberg、Hudi 等数据湖和 MySQL、Elasticsearch 等数据库的联邦查问剖析。
- 导入形式多样,反对从 HDFS/S3 等远端存储批量导入,也反对读取 MySQL Binlog 以及订阅音讯队列 Kafka 中的数据,还能够通过 Flink Connector 实时/批次同步数据源(MySQL,Oracle,PostgreSQL 等)到 Doris。**
- 社区目前 Apache Doris 社区沉闷、技术交换更多,SelectDB 针对社区有专职的技术支持团队,在应用过程中遇到问题均能疾速失去响应解决。
同时咱们也利用 Doris 的个性,解决了架构 1.0 中较为突出的问题。
- 数仓层:Apache Doris 的 Aggregate 数据模型可反对局部列实时更新,因而咱们去掉了 DWM 集市层的构建,间接增量到 Doris / ES 中构建宽表,解决了架构 1.0 中上游数据更新提早导致整个宽表提早的问题,进而晋升了数据的时效性。数据(指标、标签等)通过 Spark 对立离线加载到 Kafka 中,应用 Flink 将数据增量更新到 Doris 和 ES 中(利用 Flink 实现进一步的聚合,加重了 Doris 和 ES 的更新压力)。
- 减速层:该层次要将大宽表拆为小宽表,依据更新频率配置不同的分区策略,减小数据冗余带来的存储压力,进步查问吞吐量。Doris 具备多表查问和联邦查问性能个性,能够利用多表关联个性实现组合查问。
- 应用层:DataSet 对立指向 Doris,Doris 反对表面查问,利用该个性可对 ES 引擎间接查问。
架构 2.0 存在的问题:
- DataSet 灵便度较高,数据分析师可对指标和标签自由组合和定义,然而不同的分析师对同一数据的定义不尽相同、定义口径不统一,导致指标和标签不足对立治理,这使得数据管理和应用的难度都变高。
- Dataset 与物理地位绑定,应用层无奈进行通明优化,如果 Doris 引擎呈现负载较高的状况,无奈通过升高用户查问防止集群负载过高报错的问题。
数据架构 3.0
针对指标和标签定义口径不对立,数据应用和治理难度较高的问题,咱们持续对架构进行降级。数据架构 3.0 次要的变动是引入了专门的语义层,语义层的次要作用是将技术语言转换为业务部门更容易了解的概念,目标是将标签 (tag)与指标(metric)变为“一等公民”,作为数据定义与治理的根本对象。
引入语义层的劣势有:
- 对于技术来说,应用层不再须要创立 DataSet,从语义层可间接获取特定内容对象的标签集 (tagset)和指标集(metricset) 来发动查问。
- 对于数据分析师来说,可对立在语义层定义和创立衍生的指标和标签,解决了定义口径不统一、治理和应用难度较高的问题。
- 对于业务来说,无需消耗过长时间思考什么场景应抉择哪个数据集应用,语义层对标签和指标通明对立的定义晋升了工作效率、升高了应用老本。
存在的问题:
从架构图可知,标签和指标等数据均处于上游地位,尽管标签与指标在语义层被显式定义,但依然无奈影响上游链路,数仓层有本人的语义逻辑,减速层有本人的导入配置,这样就造成了数据管理机制的割裂。
数据架构 4.0
在数据架构 3.0 的根底上,咱们对语义层进行更深层次的利用,在数据架构 4.0 中,咱们将语义层变为架构的中枢节点,指标是对所有的指标和标签对立定义,从计算-减速-查问实现中心化、标准化治理,解决数据管理机制割裂的问题。
语义层作为架构中枢节点所带来的变动:
- 数仓层:语义层接管 SQL 触发计算或查问工作。数仓从 DWD 到 DWS 的计算逻辑将在语义层中进行定义,且以单个指标和标签的模式进行定义,之后由语义层来发送命令,生成 SQL 命令给数仓层执行计算。
- 减速层:从语义层接管配置、触发导入工作,比方减速哪些指标与标签均由语义层领导。
- 应用层:向语义层发动逻辑查问,由语义层抉择引擎,生成物理 SQL。
架构劣势:
- 能够造成对立视图,对于外围指标和标签的定义进行对立查看及治理。
- 应用层与物理引擎实现解耦,可进一步对更加灵便易用的架构进行摸索:如何对相干指标和标签进行减速,如何在时效性和集群的稳定性之间均衡等。
存在的问题:
因为以后架构是对单个标签和指标进行了定义,因而如何在查问计算时主动生成一个精确无效的 SQL 语句是十分有难度的。如果你有相干的教训,期待有机会能够一起摸索交换。
优化教训
从上文已知,为更好地实现业务需要,数据架构演进到 4.0 版本,其中 Apache Doris 作为剖析减速场景的解决方案在整个零碎中施展着重要的作用。接下来将从场景需要、数据导入、查问优化以及老本优化四个方面登程,分享基于 Doris 的读写优化教训,心愿给读者带来一些参考。
场景需要
目前咱们有 800+ 标签, 1300+ 指标,对应 TDW 中有 80 + Source 表,单个标签、指标的最大基数达到了 2 亿+。咱们心愿将这些数据从 TDW 减速到 Doris 中实现标签画像和指标的剖析。从业务的角度,须要满足以下要求:
- 实时可用:标签/指标导入当前,需实现数据尽快可用。不仅要反对惯例离线导入 T+1 ,同时也要反对实时打标场景。
- 局部更新:因每个 Source 表由各自 ETL 工作产出对应的数据,其产出工夫不统一,并且每个表只波及局部指标或标签,不同数据查问对时效性要求也不同,因而架构须要反对局部列更新。
- 性能高效:具备高效的写入能力,且在圈选、洞察、报表等场景能够实现秒级响应。
- 管制老本:在满足业务需要的前提下,最大水平地降低成本;反对冷热数据精细化治理,反对标签灵便高低架。
数据导入计划
为了加重 Doris 写入压力,咱们思考在数据写入 Doris 之前,尽量将数据生成宽表,再写入到 Doris 中。针对宽表的生成,咱们有两个实现思路:第一个是在 TDW 数仓中生成宽表;第二个是 Flink 中生成宽表。咱们对这两个实现思路进行了实际比照,最终决定抉择第二个实现思路,起因如下:
在 TDW 中生成宽表,尽管链路简略,然而弊病也比拟显著。
- 存储老本较高, TDW 除了要保护离散的 80 +个 Source 表外,还需保护 1 个大宽表、2 份冗余的数据。
- 实时性比拟差,因为每个 Source 表产出的工夫不一样,往往会因为某些提早比拟大的 Source 表导致整个数据链路提早增大。
- 开发成本较高,该计划只能作为离线形式,若想实现实时形式则须要投入开发资源进行额定的开发。
而在 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 会将屡次写入的数据进行聚合,最终用户查问时,返回一份聚合后的残缺且正确的数据。
另外两种数据模型实用的场景,这里也进行简略的介绍:
- Unique 模型实用于须要保障 Key 唯一性场景,同一个主键 ID 屡次导入之后,会以 append 的形式进行行级数据更新,仅保留最初一次导入的数据。在与社区进行沟通后,确定后续版本 Unique 模型也将反对局部列更新。
- Duplicate 模型区别于 Aggregate 和 Unique 模型,数据齐全依照导入的明细数据进行存储,不会有任何预聚合或去重操作,即便两行数据完全相同也都会保留,因而 Duplicate 模型实用于既没有聚合需要,又没有主键唯一性束缚的原始数据存储。
确定数据模型之后,咱们在建表时如何对列进行命名呢?能够间接应用指标或者是标签的名称吗?
在应用场景中通常会有以下几个需要:
- 为了更好地表白数据的意义,业务方会有大量批改标签、指标名称的需要。
- 随着业务需要的变动,标签常常存在上架、下架的状况。
- 实时新增的标签和指标,用户心愿数据尽快可用。
Doris 1.1.3 是不反对对列名进行批改的,如果间接应用指标/标签名称作为列名,则无奈满足上述标签或指标更名的需要。而对于高低架标签的需要,如果间接以 drop/add column 的形式实现,则会波及数据文件的更改,该操作耗时耗力,甚至会影响线上查问的性能。
那么,有没有更轻量级的形式来满足需要呢?接下来将为大家分享相干解决方案及收益:**
- 为了实现大量标签、指标名称批改,咱们用 MySQL 表存储相应的元数据,包含名称、全局惟一的 ID 和高低架状态等信息,比方标签歌曲名称
song_name
的 ID 为 4,在 Doris 中存储命名为 a4,用户应用更具备业务含意song_name
进行查问。在查问 Doris 前,咱们会在查问层将 SQL 改写成具体的列名 a4。这样名称的批改只是批改其元数据,底层 Doris 的表构造能够放弃不变。 - 为了实现标签灵便高低架,咱们通过统计标签的应用状况来剖析标签的价值,将低价值的标签进入下架流程。下架指的是对元信息进行状态标注,在下架标签从新上架之前,不会持续导入其数据,元信息中数据可用工夫也不会发生变化。
- 对于实时新增标签/指标,咱们基于名称 ID 的映射在 Doris 表中事后创立适量 ID 列,当标签/指标实现元信息录入后,间接将预留的 ID 调配给新录入的标签/指标,防止在查问高峰期因新增标签/指标所引起的 Schema Change 开销对集群产生的影响。经测试,用户在元信息录入后 10 分钟内就能够应用相应的数据。
值得关注的是,在社区近期公布的 1.2.0 版本中,减少了 Light Schema Change 性能, 对于增减列的操作不须要批改数据文件,只须要批改 FE 中的元数据,从而能够实现毫秒级的 Schame Change 操作。同时开启 Light Schema Change 性能的数据表也能够反对列名的批改,这与咱们的需要非常匹配,后续咱们也会及时降级到最新版本。
写入优化
接着咱们在数据写入方面也进行了调整优化,这里几点小教训与大家分享:
- Flink 预聚合:通过主键 ID 预聚合,缩小写入压力。(前文已阐明,此处不再赘述)
- 写入 Batch 大小自适应变更:为了不占用过多 Flink 资源,咱们实现了从同一个 Kafka Topic 中生产数据写入到不同 Doris 表中的性能,并且能够依据数据的大小主动调整写入的批次,尽量做到攒批低频写入。
- Doris 写入调优:针对- 235 报错进行相干参数的调优。比方设置正当的分区和分桶(Tablet 倡议1-10G),同时联合场景对 Compaction 参数调优:
max_XXXX_compaction_threadmax_cumulative_compaction_num_singleton_deltas
- 优化 BE 提交逻辑:定期缓存 BE 列表,按批次随机提交到 BE 节点,细化负载平衡粒度。
优化背景:在写入时发现某一个 BE负载会远远高于其余的 BE,甚至呈现 OOM。联合源码发现:作业启动后会获取一次 BE 地址列表,从中随机选出一个 BE 作为 Coordinator 协调者,该节点次要负责接收数据、并散发到其余的 BE 节点,除非作业异样报错,否则该节点不会产生切换。
对于大量 Flink 作业大数据场景会导致选中的 BE 节点负载较高,因而咱们尝试对 BE 提交逻辑进行优化,设置每 1 小时缓存一次 BE 列表,每写入一个批次都随机从 BE 缓存列表中获取一个进行提交,这样负载平衡的粒度就从 job 级别细化到每次提交的批次,使得 BE 间负载更加的平衡,这部分实现咱们曾经奉献到社区,欢送大家一起应用并反馈。
- https://github.com/apache/dor...
- https://github.com/apache/dor...
- https://github.com/apache/dor...
通过以上数据导入的优化措施,使得整体导入链路更加稳固,每日离线导入时长降落了 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 秒,圈选群体再次增大甚至会呈现超时报错。经排查剖析,次要的耗时包含两方面:
- BE 从 ES 中拉取数据(默认一次拉取 1024 行),对于 100 万以上的群体,网络 IO 开销会很大。
- BE 数据拉取实现当前,须要和本地的指标表做 Join,个别以 SHUFFLE/BROADCAST 的形式,老本较高。
针对这两点,咱们进行了以下优化:
- 减少了查问会话变量
es_optimize
,以开启优化开关; - 数据写入 ES 时,新增 BK 列用来存储主键 ID Hash 后的分桶序号,算法和 Doris 的分桶算法雷同(CRC32);
- BE 生成 Bucket Join 执行打算,将分桶序号下发到 BE ScanNode 节点,并下推到 ES;
- ES 对查问出的数据进行 Bitmap 压缩,并将数据的多批次获取优化为一次获取,缩小网络 IO 开销;
- Doris BE 只拉取和本地 Doris 指标表相干 Bucket 的数据,间接进行本地 Join,防止 Doris BE 间数据再 Shuffle 的过程。
通过以上优化措施,百万分群圈选洞察查问工夫从最后的 60 秒缩短到 3.7 秒,性能显著晋升!
通过与社区沟通交流,Apache Doris 从 2.0.0 版本开始,将反对倒排索引。可进行文本类型的全文检索;反对中文、英文分词;反对文本、数值日期类型的等值和范畴过滤;倒排索引对数组类型也提供了反对,多个过滤条件能够任意进行 AND OR NOT 逻辑组合。因为高性能的向量化实现和面向 AP 数据库的精简优化,Doris 的倒排索引相较于 ES 会有 3~5 倍性价比晋升,行将在 2 月底公布的 2.0 preview 版本中可用于性能评估和性能测试,置信在这个场景应用后会有进一步的性能晋升。
老本优化
在以后大环境下,降本提效成为了企业的热门话题,如何在保障服务质量的同时降低成本开销,是咱们始终在思考的问题。在咱们的场景中,老本优化次要得益于 Doris 本身优良的能力,这里为大家分享两点:
1、冷热数据进行精细化治理。
- 利用 Doris TTL 机制,在 Doris 中只存储近一年的数据,更早的数据放到存储代价更低的 TDW 中;
- 反对分区级正本设置,3 个月以内的数据高频应用,分区设置为 3 正本 ;3-6 个月数据分区调整为 2 正本;6 个月之前的数据分区调整为1 正本;
- 反对数据转冷, 在 SSD 中仅存储最近 7 天的数据,并将 7 天之前的数据转存到到 HDD 中,以升高存储老本;
- 标签高低线,将低价值标签和指标下线解决后,后续数据不再写入,缩小写入和存储代价。
2、升高数据链路老本。
Doris 架构非常简单,只有FE 和 BE 两类过程,不依赖其余组件,并通过一致性协定来保障服务的高可用和数据的高牢靠,主动故障修复,运维起来比拟容易;
- 高度兼容 MySQL 语法,反对规范 SQL,极大升高开发人员接入应用老本;
- 反对多种联邦查问形式,反对对 Hive、MySQL、Elasticsearch 、Iceberg 等组件的联邦查问剖析,升高多数据源查问复杂度。
通过以上的形式,使得存储老本升高 42%,开发与工夫老本升高了 40% ,胜利实现降本提效,后续咱们将持续摸索!
将来布局
将来咱们还将持续进行迭代和优化,咱们打算在以下几个方向进行摸索:
- 实现自动识别冷热数据,用 Apache Doris 存储热数据,Iceberg 存储冷数据,利用 Doris 湖仓一体化能力简化查问。
- 对高频呈现的标签/指标组合,通过 Doris 的物化视图进行预计算,晋升查问的性能。
- 摸索 Doris 利用于数仓计算工作,利用物化视图简化代码逻辑,并晋升外围数据的时效性。
最初,感激 Apache Doris 社区和 SelectDB 的同学,感激其疾速响应和积极支持,将来咱们也会继续将相干成绩奉献到社区,心愿 Apache Doris 飞速发展,越来越好!