StarRocks bitmap_intersect 聚合函数详解:基于位图交集实现留存计算
2026/9/18 10:07:37 网站建设 项目流程

StarRocks bitmap_intersect 聚合函数详解:基于位图交集实现留存计算

【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks

本文聚焦 StarRocks 位图函数家族中的bitmap_intersect聚合函数:它接收一组 bitmap 输入,计算这些位图的交集并返回结果 bitmap,是留存分析、标签人群重叠计算等场景的高性能基石。阅读本文后,你将掌握bitmap_intersect的语法、与bitmap_union配合的两段式聚合写法、与bitmap_to_string联用的结果解析方法,并能结合 bitmap_intersect.h 的源码理解其底层按位与(AND)实现原理与适用边界。

函数定位:分组后的位图交集

bitmap_intersect是 StarRocks 提供的聚合函数(Aggregation Function),官方文档(docs/en/sql-reference/sql-functions/bitmap-functions/bitmap_intersect.md)对其定位是:在分组(group by)之后计算 bitmap 交集,典型应用场景是用户留存率(user retention rate)计算——即找出“今天和昨天都活跃过的用户”。

位图(Bitmap)在 StarRocks 中被广泛用于对海量用户 ID 等整型集合做压缩存储与集合运算。与普通的COUNT(DISTINCT)相比,位图方案在亿级基数场景下具有显著的内存与性能优势。bitmap_intersect正是位图集合运算中“求交集”的聚合入口,与之同族的位图函数还包括bitmap_union(并集)、bitmap_union_count(并集去重计数)、bitmap_and(两个 bitmap 的交集)、bitmap_xor(对称差集)等,完整清单可参见 bitmap-functions.mdx。

函数语法与返回值

BITMAP BITMAP_INTERSECT(BITMAP value)
  • 参数:一组BITMAP类型的值,通常来自某个分组内的多行数据;
  • 返回值BITMAP类型,即这组 bitmap 的公共元素集合(交集);
  • 语义:输入一个 bitmap 集合,求该集合中所有位图的交集并返回结果位图。若分组内只有一行输入,则交集结果即该行自身的 bitmap;若某分组不存在公共元素,则返回空位图。

从聚合器注册处(aggregate_resolver_minmaxany.cpp)可以看到,bitmap_intersect被注册为TYPE_OBJECT输入/输出类型的聚合函数,底层由AggregateFactory::MakeBitmapIntersectAggregateFunction()创建(见 aggregate_factory.hpp),其聚合状态为BitmapValuePacked——一个由BitmapValueinitial初始化标记组成的结构体。

使用示例:标签维度下的留存计算

表结构前提

bitmap_intersect的输入位图通常由位图聚合列提供。以下表结构以user_id为位图聚合列(bitmap_union作为聚合类型),tag为用户标签,date为活跃日期:

KeysType: AGG_KEY Columns: tag varchar, date datetime, user_id bitmap bitmap_union

在 StarRocks 中,这类表一般通过DUPLICATE KEY明细表配合bitmap_union(user_id)预聚合,或直接使用聚合模型表中类型为BITMAP、聚合方式为BITMAP_UNION的列来承载用户 ID 集合。

场景一:计算各标签下“今日且昨日”均活跃的用户数

-- Calculate users retention under different tags today and yesterday. select tag, bitmap_intersect(user_id) from ( select tag, date, bitmap_union(user_id) user_id from table where date in ('2020-05-18', '2020-05-19') group by tag, date) a group by tag;

这段 SQL 采用两段式聚合

  1. 内层聚合:按tag, date分组,用bitmap_union(user_id)把同一天、同一标签下的所有用户 ID 合并成该日的用户位图。bitmap_union是位图并集聚合,语义上等价于“当日活跃用户集合”;
  2. 外层聚合:按tag分组,对内层产出的每一天的位图执行bitmap_intersect,得到“两天都出现过”的公共用户集合,即留存用户。

由于bitmap_intersect是聚合函数,无法直接对两列 bitmap 做点对点运算,因此必须先在内层将每日用户合并为单行位图,外层才能以位图为单位求交集——这正是上述两段式写法的核心原因。

场景二:输出留存用户的具体 ID

bitmap_intersect返回的是BITMAP类型,无法直接以整数列表形式展示。将其与bitmap_to_string函数联用,即可把位图展开为逗号分隔的 ID 字符串:

--Find out users retained under different tags today and yesterday. select tag, bitmap_to_string(bitmap_intersect(user_id)) from ( select tag, date, bitmap_union(user_id) user_id from table where date in ('2020-05-18', '2020-05-19') group by tag, date) a group by tag;

此时每个tag分组返回一行,第二列形如1,5,23的字符串,直观展示留存用户 ID 明细。若需进一步统计留存人数,可将bitmap_to_string换成bitmap_count(bitmap_intersect(user_id))得到留存用户数,用于计算留存率(留存数 / 昨日或今日活跃总数)。

源码原理:状态机驱动的按位与聚合

bitmap_intersect的 BE 端实现位于 bitmap_intersect.h,其核心是一个继承自AggregateFunctionBatchHelper<BitmapValuePacked, BitmapIntersectAggregateFunction>的聚合器类。从源码看,它的求交逻辑非常直白,由initial标记构成一个两态状态机:

  • update()(单行更新):第一个 bitmap 输入时,用|=(按位或)直接写入状态并置initial = true;之后每来一个 bitmap,都用&=(按位与)与当前状态求交集,见 bitmap_intersect.h;
  • merge()(分片合并):多 BE / 多分片并行聚合时,将其他分片的中间状态以同样的“首次赋值、后续按位与”方式合并进当前状态,见 bitmap_intersect.h;
  • finalize_to_column():把最终 bitmap 追加到结果列,见 bitmap_intersect.h。

这里的“首次用或运算赋值”是实现细节上的一个等价技巧:对于首个输入,|=即赋值,保证空分组语义下交集起点是第一个 bitmap 本身,之后逐位求交即可收敛到公共集合。聚合函数名由get_name()返回"bitmap_intersect"(见 bitmap_intersect.h),与 SQL 中的函数名一一对应。

测试佐证:交集语义与空值处理

BE 端单测 aggregate_test.cpp 直接验证了交集语义:构造三个 bitmap({1}{2,1}{1,3}),连续updatefinalize,断言结果to_string()"1"——即三个集合的公共元素只有1。该测试同时覆盖了多行输入的累积求交路径。

另一组测试 aggregate_test.cpp 验证了nullable 输入场景:对NullableColumn包装的 bitmap 列执行bitmap_intersect,结果同样正确。这意味着在实际表中存在NULL位图值时,聚合器仍可正常工作(底层BitmapColumn以对象列形式承载位图数据)。

注意事项与适用边界

  • 输入必须为 BITMAP 类型:若原始列是整型 ID,需先用to_bitmapbitmap_hash转换为 bitmap,再参与bitmap_union/bitmap_intersect运算;
  • 留存的基数前提bitmap_intersect只负责求交,留存率分母(如昨日活跃总数)需另行用bitmap_union_countbitmap_count(bitmap_union(...))计算;
  • bitmap_and的区别bitmap_and是标量函数,对两列 bitmap 逐行求交;bitmap_intersect是聚合函数,对分组内多行 bitmap 求交,二者适用 SQL 形态不同;
  • 结果位图可能为空:当分组内各 bitmap 无公共元素时,返回空位图,配合bitmap_count得到 0;
  • 位图基数上限:StarRocks 位图单元素上限为 2^64 - 1,bitmap_intersect的结果继承位图自身的容量与序列化约束。

小结

bitmap_intersect是 StarRocks 位图分析体系中负责“求交集”的聚合算子:SQL 侧以BITMAP_INTERSECT(BITMAP value)呈现,配合内层bitmap_union预聚合即可高效完成多日留存、多标签人群重叠等集合运算;BE 侧则以BitmapValuePacked为状态、按位与为核心算子,兼顾并行分片下的正确合并。对于留存率类指标,bitmap_intersect+bitmap_to_string/bitmap_count的组合已成为标准写法,值得在离线与实时分析场景中优先选用。

关键词

BITMAP_INTERSECT, BITMAP, 位图交集, 留存计算, StarRocks 聚合函数

【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询