成本下降50%,腾讯音乐StarRocks存算分离大规模实践!

作者:腾讯音乐高级数据工程师 陈嘉奇,腾讯音乐高级运营开发工程师 高盛远

小编导读:

腾讯音乐娱乐集团是中国在线音乐娱乐服务开拓者,提供如 QQ 音乐、酷狗音乐等众多知名的移动音频产品。

随着对成本效益的关注不断增强及湖仓一体化技术不断进步,数据基建团队在 2023 年将原有的上千节点 ClickHouse 和 Druid 集群业务迁移至 StarRocks 存算分离。在效率不变的情况下,成本下降了 50%,成果显著。本文将深入探讨他们的迁移策略、实现的成果以及在此过程中积累的最佳实践。希望这些经验能够为大家提供宝贵的参考和指导!

背景介绍

腾讯音乐娱乐集团是中国在线音乐娱乐服务开拓者,提供在线音乐和以音乐为核心的社交娱乐两大服务。腾讯音乐娱乐在中国有着广泛的用户基础,拥有目前国内市场知名的移动音频产品:QQ 音乐、酷狗音乐、酷我音乐、全民 K 歌、懒人听书等产品。

数据基建团队负责搭建集团统一的数据平台,支持数据接入、数据处理、数据查询和数据资产治理等。

架构演进

腾讯音乐在 2021 年的时候为了响应业务敏捷分析的请求,对原有的数据架构进行了升级。

  • 数据分析业务 采用 ClickHouse ,打造了内部最大的 ClickHouse 集群, 集群规模上千台单日新增数据量在千亿级别。
  • 监控服务场景 通过 Apache Druid 集群为业务提供告警及实时多维分析的能力。总数据量超过 10PB

随着对成本效益的关注不断增加及湖仓一体化技术的不断进步,腾讯音乐开始构建新的湖仓一体架构以进一步达成集团元数据的统一管理和管理效率提升。新的架构除了希望解决之前数据从湖导出至 OLAP 引擎而导致的元数据碎片化问题外,我们还希望通过引入弹性伸缩和分层存储技术来更进一步降本增效。然而,当时以 ClickHouse 和 Druid 组成的架构已经无法满足这些需求:

  • ClickHouse 在数据分析业务上有着不错的性能表现,但产品演进未重点投入湖仓方向,不符合公司湖仓一体化规划。此外,其存算耦合的架构在弹性能力和资源隔离能力上都存在明显不足。
  • Apache Druid 引擎主要用于腾讯音乐的监控告警、多维分析场景下使用,根据业务场景拆分了不同规格的集群。随着数据量的积累 Druid 集群 segment 瓶颈愈发明显,historical、broker 等组件异常重启后需要较长时间恢复,严重影响服务稳定性。

为解决这些问题,技术团队基于查询性能、运维难度、社区活跃情况、湖仓能力以及资源隔离能力对当前业界的方案做了调研后最终择了 StarRocks 作为我们的计算引擎。StarRocks 的产品路径规划更符合腾讯音乐对未来湖仓一体架构的设想。

湖仓一体选型

经过深入分析对比 ClickHouse、Presto 以及 StarRocks,我们得出以下关键结论:

  1. 在解决元数据碎片化、数据一致性问题以及成本控制方面,Presto 和 StarRocks 都展现出了湖仓一体化的能力,能够满足我们对湖上数据查询和统一元数据目录的需求。
  2. 然而,与 StarRocks 相比,Presto 在查询延迟上高出 2-3 倍,这无法满足我们对敏捷分析的要求。

综合考虑成本、性能等因素,StarRocks 的统一湖仓能力更契合我们的业务需求,成为我们构建湖仓一体架构的首选。

数据迁移

在我们选择 StarRocks 存算分离架构作为我们构建湖仓一体新架构后,面临的首要工作是将原有的 ClickHouse、Druid、Doris 集群的数据进行迁移,而且还要做到业务的无感,对于我们的迁移工作提出了巨大的挑战。

为此,我们在设计迁移方案时,遵循的基本思路是从影响范围由小到大逐渐灰度,实现快速试错快速修复,并将成功经验快速推广。基于这个出发点我们优先选择了 ClickHouse 实时写入子集群作为对象,将其作为切入点。以下是我们的迁移路线图:

ClickHouse 迁移实施

在将 ClickHouse 的实时业务迁移至 StarRocks 存算分离集群的过程中,我们主要面临以下三个关键挑战:

  1. 数据持久化差异 :ClickHouse 作为存算一体的系统,其数据实时写入并持久化在本地。而存算分离架构下的 StarRocks,数据需要持久化到远端的云对象存储(COS)。这要求我们在数据写入策略、集群配置以及 COS 配置上进行相应的调整和优化。
  2. SQL 语法兼容性 :存量的查询是基于 ClickHouse 的 SQL 语法构建的。迁移至 StarRocks 意味着我们需要帮助用户将现有的 SQL 语句转换为 StarRocks 兼容的语法,以确保查询的连续性和准确性。
  3. 迁移过程中的数据一致性 :由于迁移是一个逐步进行的过程,实时数据的迁移可能会导致用户在查询时需要关联到尚未迁移的数据。为此,我们需要确保在迁移期间,系统能够支持对已迁移和未迁移数据的兼容查询。

针对问题 1, 我们与社区成员紧密合作,进行了广泛的测试,以确定在不同场景下的最佳实践。这些成果将在后续的最佳实践部分详细分享,这里暂时不展开讨论。

针对问题 2, 我们的团队开发了一个基于抽象语法树(AST)的 SQL Rewriter,它能够将 ClickHouse SQL 语句改写为 StarRocks SQL。目前,该工具支持包括作用域计算列改写、模糊列消歧、子查询自动添加别名,以及非标复杂语法改写(比如 ClickHouse 独有的窗口从句,ARRAY JOIN 从句,双引号剥离等)。此外,我们还将该项目贡献回了社区,详情可访问StarRocks SQLTransformer(https://github.com/StarRocks/SQLTransformer)。

针对问题 3, 为了支持数据关联,我们为 StarRocks 贡献了 ClickHouse JDBC Catalog 功能。通过在 StarRocks 集群中创建 ClickHouse JDBC Catalog,我们能够兼容那些需要关联未迁移数据的查询。

在线上查询流量迁移的过程中,融合查询网关可以自动将已迁移至 StarRocks 数据的查询路由到 StarRocks 集群,完美地实现业务查询无感替换。


(融合查询网关同时兼容历史 ClickHouse 查询和 StarRocks 查询)

在具体实施的时候我们考虑到当前实时集群的成本是离线的两倍,而实时表的数量是离线表的十分之一,在切换的过程中网关也需要做多源改造,可能对用户产生影响,我们决定从影响范围最小、收益最大的实时集群开始切换。这一策略旨在逐步迁移各个子集群,并在此过程中积累对 StarRocks 的实践经验。

面对规模达数百台机器的 ClickHouse 和 Druid 集群,我们的团队需要精心规划,以逐步、平稳地将业务迁移至 StarRocks,同时确保整个过程中业务的连续性和用户体验的无缝性。在查询网关层面,实现智能查询路由,确保已迁移的表查询能够无缝切换至 StarRocks 集群,或在必要时回滚至 ClickHouse。在写入层面,实施 ClickHouse 和 StarRocks 的双写策略,确保数据的实时同步。在 StarRocks 集群稳定运行后,停止向 ClickHouse 写入数据。

Druid 迁移实施

Apache Druid 之前主要是承载了监控类数据。大多数场景用到的是最新时序数据,但针对历史数据的分析查询我们也需要保证迁移过程中的查询质量和效率。整体迁移方案分为:

  1. 开启全量双写。通过 Druid 的 HTTP 接口获取 Datasource 元数据信息,用脚本工具自动化生成 StarRocks 的建表语句和 Routineload 任务语句。
  2. 按表维度进行数据迁移。这里为了保障线上查询不受影响并且提升迁移速度。我们通过分析 Druid 的存储结构采用直接解析底层 Segment 文件从而得到 csv 文件,然后用 Stream Load 接口直接导入StarRocks的方式进行迁移。具体方案如下:
  • 根据 Apache Druid 元数据中 druid_segments 表里的数据获取对应 Datasource 在指定时间段内的 Segment 文件存储路径,这里可能包含 HDFS 或者 COS。
  • 根据第一步获取到的 Segment 路径,分区从远端存储下载 Segment 文件到本地,进行解压。
  • 将解压后的数据用 Druid 工具进行解码产生 csv 格式数据。
  • 通过 StarRocks 的 StreamLoad 接口导入数据。

整体流程:下载 segment 文件—解压—解码—大文件分割—导入

迁移效果

目前已经有 9 个业务集群从原有的架构统一收敛到了 StarRocks 湖仓一体新架构,我们也进行了阶段性的总结,复盘了升级后给业务带来的价值。

性能不变

在升级后为 StarRocks 存算分离架构后,原先 ClickHouse 的实时查询 P99 在迁移到 StarRocks 后保持了跟之前几乎一样的水平,都在 8 秒。

我们原先 Druid 的实时查询 P99 在迁移到 StarRocks 后性能表现与原先持平,大表查询都在 4 秒左右;中小表秒级响应。

成本降低

在存储上,我们本地只需要存一份数据,存储成本节约了一半。在运维上,由于新架构是存算分离的,我们可以在方便的通过定时 review 推动业务下线或者缩短数据生命周期的同时快速地缩容集群来节约成本。根据我们的内部估算, 替换原有的 ClickHouse 集群后,成本降低了 40%,而替换 Druid 集群后,成本更是减少了超过 50%。

简化运维与快速弹性

新的存算分离的架构增减机器变得极为高效,集群扩缩容的时间由原来的 1 周-2周降低到了现在 1-3 天。

如果业务上的数据生命周期优化到了全集群利用率低于 85% 的情况,我们可以方便的下降机器,即时降低成本。恰恰也因为扩容非常容易,我们可以保持更高的集群利用率(现在是 85%,以前是 75%)并在超过阈值的时候快速扩容,以满足业务需求)。与以往 ClickHouse 存算一体架构相比,由于扩缩容过程较为复杂,我们不得不预留更多的缓冲空间。现在,得益于新架构的灵活性,我们能够更加精确地调整资源配置,以适应业务需求的变化,从而实现成本效益的最大化。

最佳实践

由于我们是首次在生产环境中大规模尝试 StarRocks 存算分离新架构的湖仓一体方案,我们在新架构落地过程中也积累了不少的最佳实践,这里探讨一些我们实践过程中积累的经验,相信也会对大家有不小的帮助。

数据打散到多个对象存储桶

采用 StarRocks 的存算分离架构构建湖仓一体新架构时,我们选择腾讯云对象存储 COS 作为后端存储。经过测试,我们发现 StarRocks 与 COS 的兼容性非常出色。然而,COS 对每个存储桶都设定了带宽和 IOPS 的限制,并且为了有效控制成本,我们没有选择增加带宽。

面对业务中存在的多张大表和每日上千亿的数据增量,若所有数据都存储在单一存储桶中,很容易达到带宽上限。为解决这一问题,我们采取了以下策略:

  1. 分桶存储策略 :对于数据总量达到百亿的表,我们为每张表单独申请了一个存储桶,以分散数据流量,避免单个桶的带宽被占满。
  2. 灵活的存储卷管理 :得益于 StarRocks 的 Storage Volume 机制的灵活性,我们能够轻松地将不同表的数据存储在不同的存储桶中,从而优化了数据管理和访问效率。

写入反压改造和集群吞吐配置优化

在数据写入过程中,我们遇到了一些挑战,如 StarRocks 处理能力不足,导致事务数超过 1000,以及数据库锁获取失败,这些情况可能进一步导致 FE 失去响应,影响用户查询。为应对这些问题,我们采取了以下措施:

  1. 优化写入反压机制 :在集群吞吐量无法满足写入需求时,通过优化反压机制避免写入操作对整个集群造成过大压力,确保用户查询不受影响。
  2. 分析并优化集群吞吐瓶颈 :深入分析集群的性能瓶颈,并针对性地调整集群配置参数,以提升整体处理能力。
  3. 批量写入策略 :虽然批量写入是一个常见的优化手段,但需要根据实际情况调整 batchSize 。过多的小批量写入会导致更多的首次写入和后续合并操作,从而增加事务数。因此,在平衡数据实时性、内存消耗和程序稳定性的基础上,应适当增加 batchSize ,以提高吞吐量。

优化写入吞吐

在优化写入吞吐等方面,我们也积攒了不少的实践经验,总结几点:

  1. 首先是通过 StarRocks 自带的监控可以较为快速地定位出写入瓶颈,因此,建议在实际生产中配置好监控,比如下图所示:

  1. 如果监控显示写入队列有积压,此时意味着 IO 线程池工作线程数量不足,需要根据实际情况增加如果监控显示事务提交任务队列积压,则意味着此时需要提升事务提交效率,这一般会出现在高频高并发单表导入场景,我们在实践中建议开启 Batch 提交优化,可以参考 https://zhuanlan.zhihu.com/p/686949977最后,如果业务对于延迟不是非常敏感的话,建议增大 Batch Size 并降低写入频率,有助于减轻系统负担,提升系统效率。

写入反压

我们的写入反压机制做得相对简单,写入程序会周期性检查数据库的当前事务数量,一旦事务数超过设定的阈值,程序将暂停上游数据流的消费,直到事务数回落至高水位以下。这种做法简单有效,使我们能够迅速响应告警,识别并解决性能瓶颈,从而优化集群的吞吐量。这样既确保了业务的连续性,也保障了查询操作的顺畅进行。

关注 Compaction

数据合并本质上和数据写入是一个流程(Compaction 会生成一个新版本,也走导入的写数据、commit、publish version 这套完整流程)。StarRocks 在内部为每个分区维护了一个 Compaction Score 值,它反映了分区当前数据文件合并情况,Compaction score 越高,代表了数据文件合并程度越低。

如果观察到合并分数持续上升或 compactions 任务数量持续增加,且这些趋势没有收敛的迹象,这通常意味着合并操作的速度无法跟上数据写入的速度。这时我们要分析是不是写入的batch太小导致写入次数太多,如果是合并的吞吐不够,我们可以考虑增加合并的线程数。具体操作可以参考《极速查询:StarRocks 存算分离 Compaction 原理 & 调优指南

使用物化视图加速查询,降低复杂度

我们在湖仓一体新架构中也在积极探索物化视图给业务带来的巨大价值,目前在以下业务场景中使用物化视图来提升效率,降低成本和复杂度:

  • 模调监控业务 :在晚高峰时段,每小时数据生成量接近 3 亿条,导致基表查询性能下降。我们通过按不同场景维度预先计算并创建了几张伪表。与原先在 Druid 引擎中相比,每增加一张伪表都需要额外消费原始流水,这会对 Kafka 和 OLAP 引擎造成较大的资源消耗。StarRocks 的异步物化视图能力允许我们在只消费一份原始数据的情况下,在 OLAP 内部进行计算,避免了额外的带宽和 Kafka 资源消耗,同时满足了业务需求。
  • 基础监控指标业务 :过去,需要通过定时 ETL 任务来单独维护和计算集中的数据。现在通过物化视图,我们可以集中管理这些数据,减少了开发和维护成本,显著提升了研发效率。

合理配置 Cache

在基于存算分离的湖仓一体新架构中,由于对象存储的高延迟特性,Local Disk Cache 是提升查询性能的最佳方案,因为可以减少对冷数据的访问。StarRocks 提供了冷数据时间配置功能,但若配置不当,比如设置 DataCache 时间为最近7天,而用户的查询需求覆盖了30天的数据,就可能引起频繁的远程数据查询。

在某些情况下,如我们所经历的,DataCache 时间和用户查询时间的不匹配可能导致频繁的冷数据读取,这不仅增加了对 COS 下行带宽的压力,甚至在极端情况下可能导致整个集群的响应能力下降。为避免这种情况,我们可以通过 StarRocks 提供的监控工具来跟踪缓存命中率。

另外,在实践中,我们发现到升级到 StarRocks 的新版本后,其默认的 Cache 机制得到了改进。新版本的缓存不仅资源消耗更少,也能更好地控制存储空间占用。通过与 StarRocks 核心研发同学的深入交流,我们对新旧缓存机制的差异有了更全面的认识。对此感兴趣的同学可以参考文章:StarRocks 存算分离 Data Cache 二三事(https://zhuanlan.zhihu.com/p/695673099)。

总结与规划

在灰度部署首个 StarRocks 集群的过程中,我们面临了功能、可用性、可观察性和性能等多方面的挑战。在这个过程中,我们与 StarRocks 社区保持了紧密的沟通与合作,获得了他们迅速且全面的技术支持,包括详尽的技术文档、日常问题解答,以及远程协助定位线上问题等。

我们也向官方贡献了自己的成果,如音乐自研的 ClickHouse SQL 改写模块,提交了 ClickHouse JDBC Catalog 的 PR ,并发现了大规模使用中的内存泄漏等潜在问题。此外,我们提出的关于用户体验的建议和需求也得到了社区的积极响应。

在迁移过程中,我们通过表级粒度的放量和回滚控制,以及线上查询流量的重放,逐步推进迁移工作,确保了用户无感知、服务稳定和业务连续性。

目前,我们已经完成了首个实时集群的迁移工作。迁移后的 StarRocks 集群在 P95 查询性能上与原 ClickHouse 集群相当,而成本降低了 40%,符合我们的预期。未来几个月,我们将继续推动更多集群的迁移,并探索更多的应用场景。我们的下一步目标是利用物化视图进行 ETL 处理和湖上外表的联动,预期这将进一步降低成本,实现更统一的元数据管理和更精简的数据架构。未来我们也将持续提高湖仓融合的程度,最终达成全面的湖仓一体化。

对 ClickHouse JDBC Catalog 功能感兴趣的可以观看 StarRocks 3.3 版本发布直播回放:StarRocks 3.3–Deep Dive into Lakehouse