初识StarRocks

268 阅读28分钟

一.First

1.介绍

StarRocks 是新一代极速全场景 MPP (Massively Parallel Processing) 数据库。StarRocks 的愿景是能够让用户的数据分析变得更加简单和敏捷。用户无需经过复杂的预处理,就可以用 StarRocks 来支持多种数据分析场景的极速分析。

StarRocks 架构简洁,采用了全面向量化引擎,并配备全新设计的 CBO (Cost Based Optimizer) 优化器,查询速度(尤其是多表关联查询)远超同类产品

StarRocks 能很好地支持实时数据分析,并能实现对实时更新数据的高效查询。StarRocks 还支持现代化物化视图,进一步加速查询。

使用 StarRocks,用户可以灵活构建包括大宽表、星型模型、雪花模型在内的各类模型。

StarRocks 兼容 MySQL 协议,支持标准 SQL 语法,易于对接使用,全系统无外部依赖,高可用,易于运维管理。StarRocks 还兼容多种主流 BI 产品,包括 Tableau、Power BI、QuickBI、FineBI 和 Smartbi。

StarRocks 是 Linux 基金会项目,采用 Apache 2.0 许可证,可在 StarRocks GitHub 存储库中找到(请参阅 StarRocks 许可证)。StarRocks(i)链接或调用第三方软件库中的函数,其许可证可在 licenses-binary 文件夹中找到;和(ii)包含第三方软件代码,其许可证可在 licenses 文件夹中找到。

2.适用场景

StarRocks 可以满足企业级用户的多种分析需求,包括 OLAP (Online Analytical Processing) 多维分析、定制报表、实时数据分析和 Ad-hoc 数据分析等。

3.架构

StarRocks 架构简洁明了,整个系统仅由两种组件组成:前端和后端。前端节点称为 FE。后端节点有两种类型,BECN (计算节点)。当使用本地存储数据时,您需要部署 BE;当数据存储在对象存储或 HDFS 时,需要部署 CN。StarRocks 不依赖任何外部组件,简化了部署和维护。节点可以水平扩展而不影响服务正常运行。此外,StarRocks 具有元数据和服务数据副本机制,提高了数据可靠性,有效防止单点故障 (SPOF)。

StarRocks 兼容 MySQL 协议,支持标准 SQL。用户可以轻松地通过 MySQL 客户端连接到 StarRocks 实时查询分析数据。

image.png

4.存算一体和存算分离

(1) 存算一体

在存算一体架构中,StarRocks 由两种类型的节点组成:FE 和 BE。

  • FE 负责元数据管理和构建执行计划。
  • BE 执行查询计划并存储数据。BE 利用本地存储加速查询,并使用多副本机制确保高数据可用性。

image.png

(2) 存算分离

在存算分离架构中,FE 提供的功能与存算一体架构中的相同。

BE 被 CN (计算节点) 取代,存储功能被转移到对象存储或 HDFS。CN 是无状态的计算节点,可以执行除存储数据外所有 BE 的功能。

image.png

5.特点

(1) MPP分布式执行框架

StarRocks 采用 MPP (Massively Parallel Processing) 分布式执行框架。在 MPP 执行框架中,一条查询请求会被拆分成多个物理计算单元,在多机并行执行。每个执行节点拥有独享的资源(CPU、内存)。MPP 执行框架能够使得单个查询请求可以充分利用所有执行节点的资源,所以单个查询的性能可以随着集群的水平扩展而不断提升。

image.png

如上图所示,StarRocks 会将一个查询在逻辑上切分为多个逻辑执行单元(Query Fragment)。按照每个逻辑执行单元需要处理的计算量,

  • 每个逻辑执行单元会由一个或者多个物理执行单元来具体实现。
  • 物理执行单元是最小的调度单位。一个物理执行单元会被调度到集群某个 BE 上执行。
  • 一个逻辑执行单元可以包括一个或者多个执行算子,如图中的 Fragment 包括了 Scan,Project,Aggregate。
  • 每个物理执行单元只处理部分数据。 由于每个逻辑执行单元处理的复杂度不一样,所以每个逻辑执行单元的并行度是不一样的,即不同逻辑执行单元可以由不同数目的物理执行单元来具体执行,以提高资源使用率,提升查询速度。

image.png

维度逻辑执行层物理执行层
抽象层级高层语义抽象底层资源绑定
输入SQL解析后的AST优化后的逻辑执行计划
输出逻辑执行计划(Query Fragment)分布式任务(Fragment Instance)
优化目标逻辑正确性、语义完整性执行效率、资源利用率

逻辑层只是sql解析的哪个ast语法树,物理层是底层最后执行的哪些逻辑执行计划

(2) CBO优化

image.png

在多表关联查询场景下,仅靠优秀的执行引擎没有办法获得最极致的执行性能。因为这类场景下,不同执行计划的复杂度可能会相差几个数量级。查询中关联表的数目越大,可能的执行计划就越多,在众多的可能中选择一个最优的计划,这是一个 NP-Hard 的问题。只有优秀的查询优化器,才能选择出相对最优的查询计划,从而实现极致的多表分析性能。

StarRocks 从零设计并实现了一款全新的,基于代价的优化器 CBO(Cost Based Optimizer) 。该优化器是 Cascades Like 的,在设计时,针对 StarRocks 的全面向量化执行引擎进行了深度定制,并进行了多项优化和创新。该优化器内部实现了公共表达式复用,相关子查询重写,Lateral Join,Join Reorder,Join 分布式执行策略选择,低基数字典优化等重要功能和优化。 目前,该优化器已可以完整支持 TPC-DS 99 条 SQL 语句。

由于全新 CBO 的支持,StarRocks 能比同类产品更好地支持多表关联查询,特别是复杂的多表关联查询,让全面向量化引擎能够发挥极致的性能。

二.理论知识

1.catalog

StarRocks 使用 Internal Catalog 来管理内部数据,使用 External Catalog 来连接数据湖中的数据。

存储在 StarRocks 中的数据都包含在 Internal Catalog 下,Internal Catalog 可以包含一个或多个数据库。 数据库用于存储、管理和操作 StarRocks 中的数据,可用于管理多种对象,包括表、物化视图、视图等。StarRocks 采用权限系统来管理数据访问权限,定义了用户对哪些对象可以执行哪些操作,提高数据安全性。

image.png

Catalog 分为 Internal catalog 和 External catalog。

  • Internal catalog 是内部数据目录: 用于管理导入至 StarRocks 中的数据以及内部的物化视图等。每个集群都有且只有一个名为 default_catalog 的 Internal catalog,包含一个或多个数据库。StarRocks 作为数据仓库存储数据,能够显著提高查询性能,尤其应对大规模数据的复杂查询分析。
  • External catalog 是外部数据目录: 用于连接数据湖中的数据。您可以将 StarRocks 作为查询引擎,直接查询湖上数据,无需导入数据至 StarRocks。

2.database

数据库是包含表、视图、物化视图等对象的集合,用于存储、管理和操作数据。

3.table --- 不同于以往的内外表

StarRocks 中的表分为两类:内部表和外部表。

(1) 内部表

内部表归属于 Internal catalog 的数据库,数据保存在 StarRocks 中。内部表由行和列构成,每一行数据是一条记录。

此处内部表的行和列为逻辑概念,在 StarRocks 中数据实际是按列存储的。 物理上,一列数据会经过分块编码、压缩等操作,然后持久化存储。

在 StarRocks 中,根据约束的类型将内部表分四种,分别是主键表、明细表、聚合表和更新表,适用于存储和查询多种业务场景中的数据,比如原始日志、实时数据、以及汇总数据。

内部表采用分区+分桶的两级数据分布策略,实现数据均匀分布。并且分桶以多副本形式均匀分布至 BE 节点,保证数据高可用。

(2) 外部表

外部表是 External catalog 中的表,实际数据存在外部数据源中,StarRocks 只保存表对应的元数据,您可以通过外部表查询外部数据。

因为starrocks不支持update操作,因此你如果想用update,那么就将表创建为外部表,如mysql,然后在mysql中去update,starrocks会同步数据

4.物化视图

Starrocks中分为同步物化视图和异步物化视图

image.png

  1. 同步物化视图:

    • 实时性高,每来一条数据就更新一次物化视图,相当于你一次既写入原表,也写入同步物化视图,写入效率有点低,且存在卡操作的情况
    • 不支持having语句、子查询、join操作,无法实现复杂操作
    • 不支持对同一列字段,不同的聚合操作,比如count(views) sum(views) max(views) 这是不可以的,只能存在一种情况
    • 同步的命中规则比较严格,只有当查询的 SELECT 字段是视图 SELECT 字段的子集,WHERE 条件是视图 WHERE 条件的超集,GROUP BY 字段与视图的 GROUP BY 字段完全一致,并且没有额外的计算逻辑时,才会选择使用同步物化视图。若where条件有字符串匹配,基本无法命中
  2. 异步物化视图:

    • 支持子查询、having、sum(a+b)、join等一系列复杂操作;
    • 可以直接查询这个表,再sum聚合指标;
    • 异步物化视图的更新时间是有延迟的,也就是查异步物化视图表的数据和查原表聚合后的指标短暂不一致 (解决方案如下)
      1. 可以单独写sql去查原表,这相当于已经同步的部分从异步物化视图拿,而未同步的部分从原表拿;
      2. 开启cp,增大cp间隔为分钟级别,采用Flink2pc提交,这样,写入是一批一批写,然后设置REFRESH ASYNC策略,最终实现类似同步更新
    • 若要对外部系统表创建异步物化视图,必须要显式指定REFRESH ASYNC [START (<start_time>)] EVERY(INTERVAL <interval>的间隔时间([]里面可以不写),且要>=60s

(1) 同步物化视图

案例如下

  1. 创建同步物化视图
CREATE MATERIALIZED VIEW store_amt AS  
SELECT store_id, SUM(sale_amt)  
FROM sales_records  
GROUP BY store_id;

注意

  • 在同步物化视图中使用聚合函数时,查询语句必须使用 GROUP BY 语句,且 SELECT LIST 中至少包含一个分组列。
  • 同步物化视图不支持对多列数据使用单个聚合函数,不支持形如 sum(a+b) 的查询语句。
  • 同步物化视图不支持对同列数据使用多个聚合函数,不支持形如 select sum(a), min(a) from table 的查询语句。
  • 同步物化视图创建语句不支持 JOIN。
  • 使用 ALTER TABLE DROP COLUMN 删除基表中特定列时,需要保证该基表所有同步物化视图中都不包含被删除列,否则无法进行删除操作。如需删除该列,则需要将所有包含该列的同步物化视图删除,然后删除该列。
  • 为一张表创建过多的同步物化视图会影响导入的效率。导入数据时,同步物化视图和基表数据将同步更新,如果一张基表包含 n 个同步物化视图,向基表导入数据时,其导入效率大约等同于导入 n 张表,数据导入的速度会变慢。
  • 当前不支持同时创建多个同步物化视图。仅当当前创建任务完成时,方可执行下一个创建任务。
  • 仅支持在default_catalog中创建物化视图。你可以选择创建物化视图通过default_catalog.database.mv或者通过set catalog <default_catalog>切换到default_catalog。
  1. 查看同步物化视图的构建状态
SHOW ALTER MATERIALIZED VIEW
  1. 查询同步物化视图的数据 因为同步物化视图本质上是基表的索引而不是物理表,所以您只能使用 Hint [_SYNC_MV_] 查询同步物化视图的内容:
SELECT * FROM store_amt [_SYNC_MV_];
+----------+----------+  
| store_id | sale_amt |  
+----------+----------+  
|        2 |     6948 |  
|        3 |     8734 |  
|        1 |     4319 |  
|        2 |     9515 |  
|        3 |     4212 |  
|        1 |     8573 |  
+----------+----------+
  1. 删除
DROP MATERIALIZED VIEW store_amt;

(2) 异步物化视图

异步物化视图是特殊的物理表,能够存储基于基表的预计算结果。当您对基表执行复杂查询时,StarRocks 可以自动复用物化视图中的预计算结果,实现查询透明加速、湖仓加速和数据建模等业务需求。物化视图分为同步物化视图和异步物化视图。其中异步物化视图能力更加强大,能够存储基于多个基表(内部表和外部表)的预计算结果,并且支持丰富的聚合算子。

语法

CREATE MATERIALIZED VIEW [IF NOT EXISTS] [database.]<mv_name>
[COMMENT ""]
-- 必须至少指定 `distribution_desc` 和 `refresh_scheme` 其中之一。
-- distribution_desc
[DISTRIBUTED BY HASH(<bucket_key>[,<bucket_key2> ...]) [BUCKETS <bucket_number>]]
-- refresh_desc
[REFRESH 
-- refresh_moment
    [IMMEDIATE | DEFERRED]
-- refresh_scheme
    [ASYNC | ASYNC [START (<start_time>)] EVERY (INTERVAL <refresh_interval>) | MANUAL]
]
-- partition_expression
[PARTITION BY 
  [ <partition_column> [,...] ] | [ <date_function_expr> ]
]
-- order_by_expression
[ORDER BY (<sort_key>)]
[PROPERTIES ("key"="value", ...)]
AS 
<query_statement>
  1. 创建案例 必须至少指定 distribution byrefresh 策略 其中之一。 支持分区

说明

  • 创建异步物化视图时必须至少指定分桶和刷新策略其中之一。
  • 您可以为异步物化视图设置与其基表不同的分区和分桶策略,但异步物化视图的分区列和分桶列必须在查询语句中。
  • 异步物化视图支持分区上卷。例如,基表基于天做分区方式,您可以设置异步物化视图按月做分区。
  • 自 v3.3.3 起,StarRocks 支持创建基于 List 分区策略的异步物化视图。
    • 您可以基于使用 List 分区或表达式分区策略创建的表来创建 List 分区的物化视图。
    • 目前,当使用 List 分区策略创建物化视图时,您只能指定一个分区键。如果基表有多个分区键,您只能选择其中一个分区键。
    • 使用 List 分区策略的物化视图的刷新行为和查询改写逻辑与使用 Range 分区策略的物化视图一致。
  • 创建物化视图的查询语句不支持非确定性函数,其中包括 rand()、random()、uuid() 和 sleep()。
  • 异步物化视图支持多种数据类型。有关详细信息,请参阅 CREATE MATERIALIZED VIEW - 支持数据类型
  • 默认情况下,执行 CREATE MATERIALIZED VIEW 语句后,StarRocks 将立即开始刷新任务,这将会占用一定系统资源。如需推迟刷新时间,请添加 REFRESH DEFERRED 参数。
  • 仅支持在default_catalog中创建物化视图。你可以选择创建物化视图通过default_catalog.database.mv或者通过set catalog <default_catalog>切换到default_catalog。
CREATE MATERIALIZED VIEW sr_test_mv2
REFRESH ASYNC
PARTITION BY day
PROPERTIES(
  "partition_ttl_number" = "2",
  "partition_refresh_number" = "1"
)
AS 
SELECT 
    s1.day,
    s2.gp,
    sum(s1.score),
    avg(s1.score),
    max(s1.max_score),
    min(s1.min_score),
    max(s2.max_gp_score)
FROM sr_test s1 join sr_test2 s2
on s1.id = s2.id and s1.day = s2.day
group by s2.gp,s1.day;
  1. 查看创建的异步物化视图的情况
# 查看当前数据仓库内的所有物化视图
SHOW MATERIALIZED VIEWS;
# 查看字段信息
desc sr_test_mv2
# 查看建视图的语句
show create table sr_test_mv2; -- 注意只有异步物化视图是一个物理表,能这么查,同步物化视图是不可以这么查询的
  1. 直接以异步物化视图为表查询
select * from sr_test_mv2;
  1. 支持在异步物化视图上创建索引

使用Bitmap索引:

-- 创建索引
CREATE INDEX <index_name> ON <mv_name>(<column_name>) USING BITMAP COMMENT '<comment>';
-- 查看创建索引进程
SHOW ALTER TABLE COLUMN;
-- 查看索引
SHOW INDEXES FROM <mv_name>;
-- 删除索引
DROP INDEX <index_name> ON <mv_name>;

使用BloomFilter索引:

-- 创建索引
ALTER MATERIALIZED VIEW <mv_name> SET ("bloom_filter_columns" = "<col1,col2,col3,...>");
-- 查看索引
SHOW CREATE MATERIALIZED VIEW <mv_name>;
-- 删除索引
ALTER MATERIALIZED VIEW <mv_name> SET ("bloom_filter_columns" = "");

5. 手动刷新异步物化视图

-- 异步方式刷新任务。  
REFRESH MATERIALIZED VIEW order_mv;  
-- 同步方式刷新任务。  
REFRESH MATERIALIZED VIEW order_mv WITH SYNC MODE;

物化视图和视图的关系:

视图(也叫逻辑视图)是虚拟表,不实际存储数据,其中所展示的数据来自于基表生成的查询结果。每次在查询中引用某个视图时,都会运行定义该视图的查询。

5.分区和分桶

image.png

分区

第一层级为分区。表中数据可以根据分区列(通常是时间和日期)分成一个个更小的数据管理单元。查询时,通过分区裁剪,可以减少扫描的数据量,显著优化查询性能。

StarRocks 提供简单易用的分区方式,即表达式分区。此外还提供较灵活的分区方式,即 Range 分区和 List 分区。

分桶

第二层级为分桶。同一个分区中的数据通过分桶,划分成更小的数据管理单元。并且分桶以多副本形式(默认为3)均匀分布在 BE 节点上,保证数据的高可用。

StarRocks 提供两种分桶方式:

  • 哈希分桶:根据数据的分桶键值,将数据划分至分桶。选择查询时经常使用的条件列组成分桶键,能有效提高查询效率。
  • 随机分桶:随机划分数据至分桶。这种分桶方式更加简单易用。

6.索引

索引是一种特殊的数据结构,相当于数据的目录。查询条件命中索引列时,StarRocks 能够快速定位到满足条件的数据的位置。

StarRocks 提供内置索引,包括前缀索引、Ordinal 索引和 ZoneMap 索引。也支持用户手动创建索引,以提高查询效率,包括 Bitmap 和 Bloom Filter 索引。

7.联邦查询--跨catalog查询

如想在一个 catalog 中查询其他 catalog 中数据,可通过 catalog_name.db_namecatalog_name.db_name.table_name 的格式来引用目标数据。举例:

  • default_catalog.olap_db 下查询 hive_catalog 中的 hive_table
SELECT * FROM hive_catalog.hive_db.hive_table;
  • hive_catalog.hive_db 下查询 default_catalog 中的 olap_table
SELECT * FROM default_catalog.olap_db.olap_table;
  • hive_catalog.hive_db 中,对 hive_tabledefault_catalog 中的 olap_table 进行联邦查询。
SELECT * FROM hive_table h JOIN default_catalog.olap_db.olap_table o WHERE h.id = o.id;
  • 在其他目录下,对 hive_catalog 中的 hive_tabledefault_catalog 中的 olap_table 进行联邦查询。
SELECT * FROM hive_catalog.hive_db.hive_table h JOIN default_catalog.olap_db.olap_table o WHERE h.id = o.id;

三.常规表操作--sql

表操作看官方文档:表设计 | StarRocks

1.说一个好玩的东西---物化视图

Materialized Views 表:简称 MVs,物化视图

使用场景:

在实际的业务场景中,通常存在两种场景并存的分析需求:对固定维度的聚合分析 和 对原始明细数据任意维度的分析。

例如,在销售场景中,每条订单数据包含这几个维度信息(itemid, soldtime, customer_id, price)。在这种场景下,有两种分析需求并存:

  1. 业务方需要获取某个商品在某天的销售额是多少,那么仅需要在维度(itemid, soldtime)维度上对 price 进行聚合即可。
  2. 分析某个人在某天对某个商品的购买明细数据。

在现有的 StarRocks 数据模型中,如果仅建立一个聚合模型的表,比如(itemid, soldtime, customer_id, sum(price))。由于聚合损失了数据的部分信息,无法满足用户对明细数据的分析需求。如果仅建立一个 Duplicate 模型,虽可以满足任意维度的分析需求,但由于不支持 Rollup,分析性能不佳,无法快速完成分析。如果同时建立一个聚合模型和一个 Duplicate 模型,虽可以满足性能和任意维度分析,但两表之间本身无关联,需要业务方自行选择分析表。不灵活也不易用。

如何使用:

使用聚合函数(如sum和count)的查询,在已经包含聚合数据的表中可以更高效地执行。这种改进的效率对于查询大量数据尤其适用。表中的数据被物化在存储节点中,并且在增量更新中能和 Base 表保持一致。用户创建 MVs 表后,查询优化器支持选择一个最高效的 MVs 映射,并直接对 MVs 表进行查询而不是 Base 表。由于 MVs 表数据通常比 Base 表数据小很多,因此命中 MVs 表的查询速度会快很多。

(1) 创建

(1)基于文档上述明细模型表,创建测试同步物化视图

CREATE MATERIALIZED VIEW test_detail_view
AS SELECT user_id,MAX(event_type),COUNT(device_code),SUM(channel) FROM detail GROUP BY user_id;

-- 查找是否创建了视图
desc detail all;

结果如图: image.png (2)创建完视图后,用户并不感知创建成功,可以通过explain来分析是否命中视图。可以看到上面物化视图对event_type字段使用max函数,那么rollup命中的数据源为创建的物化视图。

explain select max(event_type) from detail
或者
explain SELECT user_id,MAX(event_type),COUNT(device_code),SUM(channel) FROM detail GROUP BY user_id;

查询是否当前的sql语句命中了物化视图,用explain sql语句即可查看执行计划,下面有个rollup会展示是否有命中物化视图,结果如图: image.png

看到rollup发现命中的是物化视图, 问题来了:

explain
select count(channel)
       from detail;

结果如图: image.png

explain
select min(channel)
       from detail;

结果如图: image.png

原因

  1. sum()隐含了count()的部分信息,因为要计算sum(),数据库引擎必须要知道有多少行数据参与了求和
  2. sum()和count()再处理NULL值的行为上是一直的,都会忽略NULL的行
  3. 简单来说,查询优化器认为Sum和Count都有相同的操作,并且sum物化视图其实也是存在数据的统计的,优化器发现物化视图中的 SUM(channel) 可以用来满足 COUNT(channel) 的查询需求,它可能会选择使用物化视图来减少计算量。因此,可以命中count,但是max\min这些和count、sum完全不相似,并且,如果你创建了一个count的视图,但是sum该字段,并不会命中物化视图。

物化视图的构建基础:你所创建的物化视图是基于 channel 列的 sum 聚合操作。这意味着物化视图中的数据组织、预计算结果等都是围绕 channel 列展开的。

查询列的差异count(channel) 中的 channel 列正是物化视图构建所依据的列,物化视图中的数据结构和聚合信息sum与 channel 列紧密相关,查询优化器可以利用这些信息来加速查询。而 count(event_type) 中的 event_type 列与物化视图构建所使用的 channel 列没有直接关联,物化视图里的预计算结果和数据组织形式对 event_type 列的计数操作没有帮助,所以查询优化器不会选择命中该物化视图。

(2) 删除

删除正在创建的同步物化视图
可以通过取消正在进行的同步物化视图创建任务删除正在创建的同步物化视图。首先需要通过 查看同步物化视图构建状态 获取该同步物化视图的任务 ID JobID。得到任务 ID 后,需要通过 CANCEL ALTER 命令取消该创建任务。

CANCEL ALTER TABLE ROLLUP FROM sales_records (12090);

删除已创建的同步物化视图
可以通过 DROP MATERIALIZED VIEW 命令删除已创建的同步物化视图。

DROP MATERIALIZED VIEW store_amt;

2.通用聚合函数状态

StarRocks 从 v3.4.0 开始支持通用聚合函数状态。 在早期版本中,支持仅限于内置函数,如 SUMMAXMINREPLACEHLL_UNIONPERCENTILE_UNIONBITMAP_UNION,而理论上,所有内置聚合函数都可以在聚合表中使用。为了解决这一限制,引入了通用聚合状态以支持存储所有内置函数状态。

(1) 存储通用聚合状态

可以通过指定函数名称和输入参数类型在聚合表中定义通用聚合状态,以唯一标识一个聚合函数。列类型将自动推断为聚合函数的中间状态类型。

!!!千万注意:表存储的是中间状态,不是最终的值;要想获取最终值,需要单独查询

目的:节省存储空间,中间状态采用二进制压缩存储,合并也更快,实现省空间,查更快

创建语法:col_name agg_func_name(parameter1_type, [parameter2_type], ...)
写入中间状态的语法:agg_func_name_state(参数)
查询最终聚合的语法:agg_func_name_merge(col_name)

参数解析

  • col_name: 列的名称。
  • agg_func_name: 需要存储其中间状态的聚合函数的名称。
  • parameter_type: 聚合函数的输入参数类型。可以通过参数类型唯一标识该函数。
案例如下:
CREATE TABLE test_create_agg_table (
  dt VARCHAR(10),
  hll_sketch_agg ds_hll_count_distinct(varchar),  -- HLL 去重统计中间状态
  avg_agg avg(bigint),                            -- AVG 平均值中间状态(内部维护总和 + 计数)
  array_agg_agg array_agg(int),                   -- ARRAY_AGG 聚合中间状态(内部是二进制数组)
  min_by_agg min_by(varchar, bigint)              -- MIN_BY 最小值中间状态(最小 bigint 对应的 varchar)
)
AGGREGATE KEY(dt) -- 按照dt进行聚合,相同dt的记录会自动合并中间状态,按照建表时候的agg_func_name
PARTITION BY (dt) 
DISTRIBUTED BY HASH(dt) BUCKETS 4;

1. 数据导入时的自动预聚合
当向该表插入数据时,StarRocks 会自动调用 _state 函数生成中间状态:
INSERT INTO test_create_agg_table
SELECT 
    dt,
    ds_hll_count_distinct_state(user_id),  -- 将user_id转换为 HLL 中间状态,相同dt的会被自动合并
    avg_state(age),                        -- 将age转换为 AVG 中间状态(sum(age)和count(age)),写入时自动累加
    array_agg_state(order_id),             -- 将order_id转换为 ARRAY_AGG 中间状态,自动累加
    min_by_state(city, revenue)            -- 维护revenue最小的city值转换为 MIN_BY 中间状态,自动比较更新最小值
FROM raw_data
GROUP BY dt;

2. 查询时的透明聚合
查询时,StarRocks 自动调用 _merge 函数生成最终结果:
SELECT 
    dt,
    ds_hll_count_distinct_merge(hll_sketch_agg) AS uv,   -- 输出基数统计结果
    avg_merge(avg_agg) AS avg_age,                       -- 输出平均值
    array_agg_merge(array_agg_agg) AS order_list,        -- 输出数组
    min_by_merge(min_by_agg) AS min_city                 -- 输出最小 revenue 对应的 city
FROM test_create_avg_table
GROUP BY dt;

总结个流程

  1. 建表的时候,用: 字段名 聚合函数名(参数类型)去创建,如avg_agg avg(bigint)
  2. insert的时候:用 聚合函数名_state(参数) 去写入,这样就写入的是中间状态,如avg_state(age),那么表中存储的就是sum(age)和count(age) 假设是300和5
  3. 查询最终结果: 用 聚合函数名_merge(表字段名),这样就会按照表中的中间状态去进行聚合,如avg_merge(avg_agg) 最终获取到的就是(300/5)=60

(2) 讲解一下组合函数,就是刚才那些后缀函数

后缀名功能描述适用场景
_state生成聚合中间状态(非最终结果),用于存储部分聚合数据预聚合存储、跨分片中间结果传递
_merge合并多个中间状态(如多批次导入或分布式节点的部分聚合结果)多版本数据合并、分布式聚合计算
_union对特殊数据类型(如 HLL、BITMAP)执行集合运算(并集、去重等)基数统计、近似去重
_finalize将中间状态转换为最终可读结果(如 HLL 转基数、BITMAP 转精确计数)查询结果输出

state和merge在上面已经举例子了,下面讲解union和finalize

<1> _union 函数将多个中间状态列合并为一个新的中间状态。

_union 是一个聚合函数。返回中间状态类型,而不是函数最终结果的类型。

-- 合并多个聚合中间状态。
定义:agg_intermediate_type {agg_func_name}_union(input_col)

参数解析

  • agg_func_name: 聚合函数的名称。
  • input_col: 聚合函数的输入列。输入列类型是聚合函数的中间状态类型。您可以使用 _state 函数获取它。
  • agg_intermediate_type: _union 函数的返回类型,即聚合函数的中间状态类型。

假设现在test_create_agg_table表中的值如下 下面这俩相同dt的不是没聚合,而是两个分片,不同分片的数据不会聚合

分片dthll_sketch_aggavg_aggarray_agg_aggmin_by_agg
分片 12025-06-27HLL_Sketch(1、2)sum=45, count=2[101, 102](北京,100)
分片 22025-06-27HLL_Sketch(3、4)sum=65, count=2[103, 104](广州,50)
-- 案例1:合并聚合表中的中间状态
SELECT 
    dt,
    ds_hll_count_distinct_union(hll_sketch_agg),  -- 合并 HLL 中间状态
    avg_union(avg_agg),                           -- 合并 AVG 中间状态(总和=10100,计数=200)
    array_agg_union(array_agg_agg),               -- 合并 ARRAY_AGG 中间状态(合并所有数组)
    min_by_union(min_by_agg)                      -- 合并 MIN_BY 中间状态(取全局最小 id 的 province)
FROM test_create_agg_table
GROUP BY dt
LIMIT 1;

结果(中间状态):2025-06-27 | HLL_Sketch(1234) | sum=110, count=4 | [101, 102, 103, 104] | (广州,50) |

-- 案例2:直接合并原始数据的中间状态(跳过存储)
假设t1表的数据如下
| 2025-06-27 | 1 | 北京 |
| 2025-06-27 | 2 | 上海 |
| 2025-06-27 | 3 | 广州 |
| 2025-06-27 | 4 | 深圳 |
SELECT 
    dt,
    ds_hll_count_distinct_union(ds_hll_count_distinct_state(id)),
    avg_union(avg_state(id)),
    array_agg_union(array_agg_state(id)),
    min_by_union(min_by_state(province, id))
FROM t1
GROUP BY dt
LIMIT 1;

结果:2025-06-27 | HLL_Sketch(1234) | sum=10, count=4 | [1, 2, 3, 4] | (北京,1) |

解析
1.  **HLL 状态**:直接对 `id=1234` 生成 HLL Sketch → `HLL(1234)`
2.  **AVG 状态**:`sum=1+2+3+4=10`,`count=4`
3.  **ARRAY 状态**:收集 `id=1234` 到数组 → `[1, 2, 3, 4]`
4.  **MIN_BY 状态**:`id=1` 最小,对应 `province=北京`
<2> _finalize:中间状态->最终结果的"临门一脚"

这个时候就有疑问了,merge也可以得到最终结果啊,为什么还要你这个finalize?

函数作用阶段输入输出
_merge中间状态合并多个中间状态合并后的中间状态
_finalize结果输出合并后的中间状态业务可读的最终结果

merge是先中间再合并;finalize则是直接合并

假设test_create_agg_table表的数据如下

分片dthll_sketch_aggavg_aggarray_agg_aggmin_by_agg
分片 12025-06-27HLL_Sketch(1、2)sum=45, count=2[101, 102](北京,100)
分片 22025-06-27HLL_Sketch(3、4)sum=65, count=2[103, 104](广州,50)
SELECT 
    dt,
    ds_hll_count_distinct_finalize(
        ds_hll_count_distinct_union(hll_sketch_agg)
    ) AS uv,  -- 最终 UV(去重用户数)
    avg_finalize(
        avg_union(avg_agg)
    ) AS avg_id,  -- 最终平均 ID
    array_agg_finalize(
        array_agg_union(array_agg_agg)
    ) AS all_ids,  -- 最终 ID 数组
    min_by_finalize(
        min_by_union(min_by_agg)
    ) AS min_province  -- 最小 ID 对应的省份
FROM test_create_agg_table
GROUP BY dt;

结果:2025-06-27 | 4 | 27.5 | [101, 102, 103, 104] | 广州 |

(3) 优缺点、

维度聚合表(预聚合)通用聚合函数状态表(中间状态)
查询性能预聚合结果直接返回,查询速度极快,适合高频报表场景。查询时需动态合并中间状态,计算开销较高,尤其大数据量时。
存储成本需存储最终聚合结果,存储空间通常小于原始数据,但可能因聚合维度多而增加存储。存储中间状态(如 HLL sketch、AVG 的 sum/count),部分场景下存储量可能更小(如 HLL 压缩率高)。
灵活性聚合规则在建表时固定,修改需重建表,灵活性低。支持动态选择聚合函数(如 HLL、MIN_BY 等),无需修改表结构,灵活性高。
适用场景固定维度的聚合统计(如按时间、地域分组的总和、计数)。复杂聚合函数(HLL 去重、TOP N、自定义聚合)或需要跨分片合并中间状态的场景。
数据导入性能导入时自动聚合,可能减少数据量,提升导入效率。导入时仅存储中间状态,无需实时聚合,导入速度可能更快(取决于聚合函数复杂度)。
计算资源消耗查询时几乎不消耗计算资源,依赖存储换计算。查询时需实时计算合并,消耗 CPU 和内存资源。

聚合表:用空间换时间,查询快,写入慢一点,存储大

通用聚合函数状态表:用计算换空间,查询慢一点,写入快,存储小