作者:滴滴 OLAP 开发工程师 刘雨飞
01
实时数据洞察在业务管理中的重要性无可替代。在滴滴内部,网约车实时看板是公司最关键的业务监控工具之一,包含着超过 20 个重要的业务指标,如实时呼叫量、冒泡数量和 GMV 等。这些实时看板数据对于业务、数据和运营团队至关重要,提供了即时见解,帮助我们更好地了解业务的状态和趋势。
通过实时监测这些数据,我们能够迅速应对市场波动,做出及时决策,例如:
然而,关键业务指标的计算通常需要大量的精确去重操作,尤其在高峰访问期间,这可能会对资源造成巨大压力,也是众多 OLAP 系统一直以来的挑战之一。为了平衡计算成本,许多系统牺牲了准确性,提供模糊去重的方法,这也是滴滴过去基于 Druid 的指标计算方案所采用的策略。
但是,模糊去重方法存在计算误差,这意味着我们可能无法完全准确地反映业务的实际情况,从而难以实现更精细化的运营决策。此外,即便为了降低资源消耗而牺牲了准确性,模糊去重节省的资源在高并发场景下仍旧是杯水车薪。在大规模促销活动期间,高并发查询可能导致 Druid 集群的稳定性问题,引发性能下降、响应时间延长甚至系统崩溃,这对一个依赖实时数据的业务来说是不可接受的。
02
实时看板场景具有以下显著特点:
这样的业务场景对查询引擎提出了非常高的要求。自 2022 年起,滴滴已经在网约车、单车、能源、货运等多个领域应用了 StarRocks,我们也在密切关注社区的新功能。异步物化视图是针对透明查询加速、高并发场景研发的利器,自其发布之后,我们就开始了这个场景的测试验证。尝试物化视图主要是由于其以下几个特点:
03
在结合业务特点和 StarRocks 的物化视图能力,我们设计了整个看板场景的加速优化链路。以下是设计的主要思路:
为了最大限度地降低去重操作对 CPU 的消耗、加速去重,我们的整体思路是:
数据预处理:利用全局字典进行数据类型转换,用最小的代价做精确去重
ODS 层:利用明细模型存储原始数据
DWD 层:利用同步物化视图构建增量聚合层
ADS 层:利用异步物化视图构建透明加速层
接下来展开讲解每一部分是如何构建的。
01
StarRocks 支持两种类型的去重方式:Hyper-loglog 和 BITMAP。对于需要精确去重的指标,我们使用 BITMAP 类型。
StarRocks 内部采用了 Roaring BITMAP 的实现方式,字段类型要求在 INT(64) 位以内,并在数据连续性较好的情况下性能表现更佳。如果数据具有连续递增的特性,相较于完全随机的 ID 性能优势可达数倍以上。因此,滴滴在 StarRocks 中实现了高基数全局字典的功能。
这里利用到 StarRocks 主键模型表的部分列更新以及自增列功能。文档请见:
https://docs.starrocks.io/zh-cn/latest/using_starrocks/query_acceleration_with_auto_increment
第二步:字典映射函数
我们实现了字典映射的函数 dict_mapping,其入参包括字典表表名和主键值。它可以在计算时实时查询字典表,并返回生成的 ID 值。为了提高性能,我们使用了 StarRocks 的主键索引进行加速,相比于基于直接扫描,性能提升非常显著。这个函数目前已经贡献回社区,欢迎大家使用。
第三步:Flink 数据写入
对 Flink 的 flink-starrocks-connector 进行改造,数据写入分为两步:
通过这一流程,实现了在 StarRocks 中的全局字典功能,用于高效处理需要精确去重的指标计算。这个优化方案显著提高了性能并降低了计算成本,使得处理高基数去重变得更加高效和可扩展。
02
在增量聚合层我们创建了同步物化视图。同步物化视图同样提供了基于 BITMAP 和 HLL 算法的去重方式。我们可以方便地利用 BITMAP 聚合函数来创建同步物化视图。同步物化视图创建完成后,后续查询语句中的子查询 count(distinct order_id)
会被自动改写为 bitmap_union_count(to_bitmap(order_id))
以便查询命中物化视图。例如:
CREATE MATERIALIZED VIEW mv_dwd AS
SELECT dt, time_slice(`table_time`, INTERVAL 5 minute, floor) AS `ts`, city, source_type, bitmap_union(to_bitmap(order_id))
FROM base_table
GROUP BY dt, ts, city, source_type;
03
在增量聚合层我们创建了异步物化视图。这里以简化后的订单表为例,介绍如何通过创建异步视图来实现查询加速。订单表包括分区日期、数据时间、城市、渠道、业务线等维度字段,以及需要去重的字段业务订单 ID。
在数据表中,我们使用time_slice
函数将时间粒度取整为 5 分钟,并按照 5 分钟区间粒度进行数据聚合。聚合的维度包括城市、渠道等可累加的维度。这个聚合视图的好处在于,当用户需要查询 5 分钟粒度的数据,并且查询条件与视图中的聚合维度完全匹配时,可以直接使用这个视图进行查询加速,而无需查询原始底表的明细数据。示例如下:
CREATE MATERIALIZED VIEW `mv_ads`
PARTITION BY (dt)
DISTRIBUTED BY HASH(`ts`) BUCKETS 1
REFRESH ASYNC START(“2023-06-28 21:00:00”) EVERY(INTERVAL 30 SECOND) PROPERTIES (
"partition_refresh_number" = "3" )
AS SELECT
`dt`,
time_slice(`table_time`, INTERVAL 5 minute, floor) AS `ts`, `city`,
`source_type`,
count(DISTINCT `order_id`) AS `order_num`
FROM `base_table` GROUP BY
`dt`,`ts`,`city`, `source_type`;
在上述过程中,需要创建的异步物化视图数量可以通过以下方式进行优化,以降低视图建设的成本:
区分出可累加维度和不可累加维度
通过以上优化策略,如果数据表中有 M 个可累加维度列和 N 个不可累加维度列,那么只需要创建 2^(N-M) 个异步物化视图,而不是 2^N 个。这样可以大幅减少视图数量的同时满足绝大多数查询条件的加速需求,降低了建设和维护的成本,提高了系统的可维护性和效率。
04
StarRocks 提供了物化视图的透明加速功能,使用户可以无需手动选择使用哪张表,而是通过自动改写查询来实现加速,同时保持查询的语义一致性和性能提升。
假设有以下查询示例:
SELECT
ts,
SUM(order_num)
FROM(
SELECT
time_slice(`table_time`, interval 5 minute) AS ts,
count(DISTINCT `order_id`) AS `order_num`
FROM
`base_table`
WHERE
(...)
GROUP BY
`dt`,
`ts`,
`city`,
`source_type`
) sub
WHERE
dt = '2023-07-01'
GROUP BY
ts;
视图1:包括 5 分钟粒度聚合、可累加维度分区(dt)、日期(ts)、城市(city)、渠道(source_type)。
CREATE MATERIALIZED VIEW `mv_1`
PARTITION BY (dt)
DISTRIBUTED BY HASH(`ts`) BUCKETS 1
REFRESH ASYNC START(“2023-06-28 21:00:00”) EVERY(INTERVAL 30 SECOND) PROPERTIES (
"partition_refresh_number" = "3" )
AS SELECT
`dt`,
time_slice(`table_time`, INTERVAL 5 minute, floor) AS `ts`,
`city`,
`source_type`,
count(DISTINCT `order_id`) AS `order_num`
FROM
`base_table`
GROUP BY
`dt`,
`ts`,
`city`,
`source_type`;
CREATE MATERIALIZED VIEW `mv_2`
PARTITION BY (dt)
DISTRIBUTED BY HASH(`ts`) BUCKETS 1
REFRESH ASYNC START(“2023-06-28 21:00:00”) EVERY(INTERVAL 30 SECOND) PROPERTIES (
"partition_refresh_number" = "3" )
AS SELECT
`dt`,
time_slice(`table_time`, INTERVAL 5 minute, floor) AS `ts`,
`city`,
`source_type`,
`product_line`,
count(DISTINCT `order_id`) AS `order_num`
FROM
`base_table`
GROUP BY
`dt`,
`ts`,
`city`,
`source_type`
`product_line`;
WHERE
条件为空,则命中视图1
,并自动改写查询以使用该视图加速。WHERE
条件包含 city IN (...)
,则命中视图1
,并相应地改写查询。WHERE
条件包含 product_line=?
,则命中视图2
,并自动改写查询以使用该视图。Case 4:如果 WHERE
条件包含多个业务线查询,不支持累加,无法命中视图2
,但如果有创建同步视图的话,可以进行上卷加速。
04
设计方案的核心在于权衡,而方案的成功关键在于综合性能的提升。对于看板类查询,其并发性非常高,但查询模式相对固定。大多数查询都是相似甚至重复的。因此,整体方案的思路在于在一定程度上牺牲灵活性,以保障查询性能的极致提升。相较于基于明细直接计算,基于 StarRocks 物化视图对业务监控看板进行加速优化的优点显而易见:
当前,线上通过物化视图支持了上百并发的精确去重查询,彻底解决了 Druid 仅能支持几十并发模糊去重的问题,相较于原有架构 QPS 提升了 10 倍。然而,方案也存在一些明显的不足之处,包括:
未来,我们会联合社区进一步优化物化视图,提高效率:
并且,社区也在研发更加高效的全局字典,通过将字典表直接缓存在 BE/CN 的内存上,以降低查询字典时的网络开销,以满足除精确去重之外的更多场景。
💬 StarRocks Feature Group-MV:
扫码加入我们的“StarRocks 物化视图用户小组”👇🏻
StarRocks 物化视图用户小组
关于 StarRocks