芒果TV x StarRocks:极速统一的流批处理架构全新进化,助力数据分析乘风破浪

本文发表于: &{ new Date(1687968000000).toLocaleDateString() }

StarRocks 小编导语:

在引入 StarRocks 之前,芒果 TV 的智慧运营平台架构采用云上 EMR 平台,Hive 存储历史数据,Kudu 存储实时数据,用 Presto 做统一的查询引擎。随着业务复杂度增加,该架构面临很大的挑战,架构分散复杂,业务开发运维成本很高,查询性能也逐步遇到瓶颈。在升级到 StarRocks 统一湖仓架构后,极大的简化了整体数据分析架构,同时综合查询性能提升 10+ 倍。

芒果 TV 是湖南广电旗下的互联网视频平台,为年轻用户带来新鲜综艺和内容剧集的高质量长视频内容。小芒是芒果 TV 旗下新潮国货内容电商平台,也是芒果 TV 长视频内容 IP 电商变现之路上的一颗新星。芒果智慧运营平台主要负责芒果 TV 会员、广告业务以及小芒的数据建设,为数据分析人员提供自助的实时数据用户行为分析,业务数据的个性化报表搭建,自定义用户分群计算等数据分析服务,致力于打破数据孤岛,驱动产品和业务智能与增长。

 

01 原有架构与痛点

智慧运营平台数据源主要有用户行为数据与业务数据。用户行为数据由客户端埋点上报,通过 Flume 发送到 OSS 与 Kafka 原始日志 topic。OSS 数据清洗后写入 Hive,作为离线历史数据。Kafka 数据通过 Flink 实时清洗后写入 Kudu,作为实时数据。业务数据主要是后端服务业务库中的 MySQL 数据,通过自研数据同步平台,实时同步到 Kudu。智慧运营平台使用 Presto 作为查询引擎,将历史数据与实时数据合并,再与业务数据进行关联,提供自助式实时查询服务。

原有的技术架构中,Kudu 存储引擎满足秒级低延迟批量数据插入与实时查询。数据由离线部分(Hive 表)和实时数据(Kudu 表)两部分组成,Kudu 中只保留少部分数据,这样 Kudu 存储引擎数据合并压力会小很多,整个系统也就更加稳定。公司各部门都有自己维护的业务数据表,Presto 的联邦查询能够帮助快速的打通各业务数据。Presto 架构简单,能够快速扩容应对流量压力。

随着数据业务的不断发展,用户查询的数据量、Query 的复杂度,查询并发度都急剧增大,原有架构存在一些问题:

  1. Presto 查询性能一般,无法满足业务方希望能快速获取数据的需求。
  2. 数据关联组件多、维护成本高。
  3. 资源使用成本较高。
  4. Presto 高并发支持不够,coordinator 容易成为瓶颈。
  5. 缺少 Bitmap 数据类型,在标签计算方面存在一些不足。

02 引入 StarRocks

在 2022 年年底我们开始新的技术架构探讨和规划,我们秉承“既要”、“又要”原则。新的数据架构既要能解决当前架构的问题,又要能够满足未来数仓存算分离与引擎一体化的要求。

数据架构的选择本质上就是数据引擎的选择。那么满足我们“既要”“又要”要求的理想数据引擎需要达到哪些条件呢?下面这些是我们选择新的数据引擎的标准:

高稳定性:在商业产品中,数据平台的稳定性大于一切,抛开系统稳定性去谈查询性能优化毫无意义,我们只有在保障系统高可用的前提下,再去考虑数据查询的效率和性能。

架构简单,维护成本低:最近几年大火的 Iceberg 和 Hudi 开源数据湖解决方案就是因为其维护和管理成本较高,我们最终放弃选择 Iceberg 和 Hudi。

联合查询效率高:在我们业务实践中,仅查询单个大宽表的场景非常少,用户经常需要联合查询多张业务表,这个时候就考验数据引擎 Join 联查效率了,ClickHouse 在单表查询的时候性能极高,但是一旦涉及多表联查,其查询效率就会急剧下降。基于我们业务联合查询占比非常高,我们在数据引擎选择上也就放弃了ClickHouse。

支持联邦查询:因为我们有几千上万张的历史 Hive 表,而且我们的业务也需要这部分数据,所以我们希望新的查询引擎能够和 Presto 一样,支持不同数据源的 Catalog 查询,降低我们整体架构迁移成本。

综合查询效率高:我们目前在使用 Presto 查询的时候有一个问题就是查询耗时长,产品和运营同学会经常反馈查询慢,所以我们希望找到一款查询性能上有极致优化的引擎,能够大大降低查询响应时间,让数据平台能够给使用者提供一个较好的体验。

自生态:尽可能少的依赖外部组件,比如不需要依赖 Hadoop 和 ZooKeeper 等这些庞大的大数据基础组件,整套系统自身就会形成闭环的数据生态。

存算分离:由于业务不断接入和发展,数据越来越多,传统的存算一体方案需要使用到本地磁盘和多副本机制。这就带来了居高不下存储成本,相比之下云厂商的对象存储不仅在成本上只有本地磁盘的 1/10 而且还不需要考虑多副本机制。所以未来的数据引擎,必须是拥有存算分离架构的。

综上所述,我们在 2023 年 Q1 季度对多种数据引擎进行综合调研对比,StarRocks 因其稳定性高、查询速度快并且拥有完备的存算分离架构特点,成为了我们新的数据架构核心引擎最终选择。

具体使用方式如下图所示,行为数据先通过 Flink 清洗后写入 Kafka 清洗日志 topic,再通过 Routine Load 将数据导入 StarRocks。业务数据通过同步平台,采用 Stream Load 导入全量数据+Flink 消费 Canal Binlog 增量数据的方式同步。

03 实践经验总结

  1. 实时同步业务库 MySQL 数据到 StarRocks:MySQL 数据会频繁的 update/delete,部分表进行了分库分表设计,全量数据超过 20 亿,且主键为字符串类型。综合内存占用、性能等考虑,采用了主键模型+索引持久化,避免因主键索引占用 BE 内存过大而导致查询内存溢出。
  2. 利用物化视图加速查询:针对一些查询频率高,耗时长的查询,构建物化视图,在查询无感知的情况下实现性能加速。一些耗时几十秒到数分钟的查询,利用物化视图加速到 1 秒以内。
  3. 利用聚合模型优化数据统计:以明细数据的方式将数据实时导入聚合模型表,完成部分数据指标的聚合,降低统计延时。将原来需要按分钟、小时定时聚合的数据,降低到毫秒级实时聚合。
  4. 利用 Bitmap 类型,实现秒级海量用户标签圈选功能:
    在一些需要快速圈人场景下,利用 Bitmap 的位计算可以极大提升效率与降低资源使用。Bitmap 使用要求存入的数据为数字类型,在我们的业务场景中,都是根据用户 uuid/did 来进行选择,需要将字符串类型的 uuid/did 转换为数字类型。利用 StarRocks 的自增列功能,结合主键模型,部分列更新功能,实现全局字典表功能。通过 Bitmap 函数,进行用户标签圈选人数计算、用户导出,将原来耗时数秒甚至数分钟的查询,降低到毫秒级别。
#1 导入数据到用户自增ID表
使用routine load,将用户行为数据导入自增ID表,设置部分列更新 "partial_update" = "true"

#2 用户自增ID表建表语句示例
CREATE TABLE `auto_user` (
`did` varchar(64) NOT NULL COMMENT "",
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT "自增id"
) ENGINE=OLAP
PRIMARY KEY(`did`)
COMMENT "用户自增ID表"
DISTRIBUTED BY HASH(`did`) BUCKETS 6;

#3 日活跃用户bitmap表建表语句示
CREATE TABLE `active_user` (
`date` int(11) NOT NULL COMMENT "",
`bm_ids` bitmap BITMAP_UNION NOT NULL COMMENT ""
) ENGINE=OLAP
AGGREGATE KEY(`date`)
COMMENT "日活跃用户bitmap表"
DISTRIBUTED BY HASH(`date`) BUCKETS 3;

#4 插入数据到活跃用户表
insert into active_user select date,to_bitmap(b.id) from event a left join auto_user b on a.did=b.did where `date`=20230501;

#5 圈人查询
查询5月1-7号都活跃的用户数
select bitmap_count(bitmap_intersect(bm_ids)) from active_user where `date`>=20230501 and `date`<=20230507;
查询5月1-7号都活跃的用户did:
select b.id,b.did from (select unnest as id from (select bitmap_intersect(bm_ids) as bm_ids from active_user where `date`>=20230501 and `date`<=20230507) t, unnest(bitmap_to_array(bm_ids))) a inner join auto_user b on a.id=b.id;

利用 Query Cache 保存查询的中间计算结果:

  1. 智慧运营平台的大部分查询都是不再变更的历史数据+当天的实时数据的合并,在之前的架构中,我们基于查询性能、数据实时性、数据准确性等多方面考虑,进行了复杂的缓存管理。StarRocks 的 Query Cache,能将基于 Per-tablet 计算的中间结果进行缓存,支持针对分区、多版本的缓存机制,极大提高了缓存利用率,提升了查询性能。将原有的智慧运营平台的缓存管理,交由 StarRocks 自身,使得缓存使用更加简单、高效。通过使用 Query Cache,使查询效率提升超过 50%。

选择 Zstd 作为压缩格式:

  1. 综合对比了 StarRocks 支持的 4 种压缩格式,基于压缩比/查询导入性能综合对比,我们选择使用了更高性价比的 Zstd 作为数据压缩格式。(Zstd 对比 lz4 压缩比高 20%,查询写入性能低 5% 左右)

根据业务特点调整 Compaction 相关参数:

在以 LSM-Tree 为架构的系统中是非常关键的模块,在前期的测试中,我们也遇到了因 Compaction 不及时带来的多种问题。根据自身业务的数据特点,我们优化数据存储结构,调整导入、Compaction 相关参数,使 Compaction 能够及时顺利完成,保证数据写入与查询的平衡。

04 性能测试

在使用 StarRocks 优化后,我们进行了 SSB 性能测试,也基于实际业务中的一些典型 Query 进行了性能测试,StarRocks 对比原有方案都有明显的性能提升。

SSB 宽表

SSB 多表 Join

实际业务 Query

05 未来规划

StarRocks 在 3.0 版本中推出了存算分离功能,使得 StarRocks 作为云原生数据湖方案成为了可能。存算分离在存储成本、高可用、快速扩缩容都带来了质的提升,我们也计划在未来构建基于 StarRocks 的云原生数据湖仓,以 StarRocks 为底座,构建新一代数据平台。