StarRocks支持四种表类型,分别是主键表 ( Primary Key table)、明细表 (Duplicate key table)、聚合表 (Aggregate table)和更新表 (Unique Key table) 。这四种表类型能够支持多种数据分析场景,例如实时分析、日志分析、数据汇总分析等。
1. 主键表(Primary Key Table)
1.1 简介
主键表使用 StarRocks 全新设计开发的存储引擎。其主要优势在于支撑实时数据更新的同时,也能保证高效的复杂即席查询性能。在实时分析业务中采用主键表,用最新的数据实时分析出结果来指导决策,使得数据分析不再受限于 T+1 数据延迟。
主键表中的主键具有唯一非空约束,用于唯一标识数据行。如果新数据的主键值与表中原数据的主键值相同,则存在唯一约束冲突,此时新数据会替代原数据。
1.2 适用场景
实时分析、用户画像等
实时对接事务型数据至 StarRocks。事务型数据库中,除了插入数据外,一般还会涉及较多更新和删除数据的操作,因此事务型数据库的数据同步至 StarRocks 时,建议使用主键表。通过 Flink-CDC 等工具直接对接 TP 的 Binlog,实时同步增删改的数据至主键表,可以简化数据同步流程,并且相对于 Merge-On-Read 策略的更新表,查询性能能够提升 3~10 倍。
利用部分列更新轻松实现多流 JOIN。在用户画像等分析场景中,一般会采用大宽表方式来提升多维分析的性能,同时简化数据分析师的使用模型。而这种场景中的上游数据,往往可能来自于多个不同业务(比如来自购物消费业务、快递业务、银行业务等)或系统(比如计算用户不同标签属性的机器学习系统),主键表的部分列更新功能就很好地满足这种需求,不同业务直接各自按需更新与业务相关的列即可,并且继续享受主键表的实时同步增删改数据及高效的查询性能。
1.3 Key设计
- 在建表语句中,主键列必须定义在其他列之前。
- 主键必须包含分区列和分桶列。
- 主键列支持以下数据类型:数值(包括整型和布尔)、日期和字符串。
- 默认设置下,单条主键值编码后的最大长度为 128 字节。
- 建表后不支持修改主键。
- 主键列的值不能更新,避免破坏数据一致性。
1.4 创建语句示例
js
CREATE TABLE orders (
order_id bigint NOT NULL,
dt date NOT NULL,
merchant_id int NOT NULL,
user_id int NOT NULL,
good_id int NOT NULL,
good_name string NOT NULL,
price int NOT NULL,
cnt int NOT NULL,
revenue int NOT NULL,
state tinyint NOT NULL
)
PRIMARY KEY (order_id,dt,merchant_id)
PARTITION BY date_trunc('day', dt)
DISTRIBUTED BY HASH (merchant_id)
ORDER BY (dt,merchant_id)
PROPERTIES (
"enable_persistent_index" = "true"
);
2. 明细表(Duplicate Key Table)
2.1 简介
明细表是默认创建的表类型。如果在建表时未指定任何 key,默认创建的是明细表。
建表时支持定义排序键。如果查询的过滤条件包含排序键,则 StarRocks 能够快速地过滤数据,提高查询效率。
明细表适用于日志数据分析等场景,支持追加新数据,不支持修改历史数据。
2.2 适用场景
分析原始数据,例如原始日志、原始操作记录等。
查询方式灵活,不需要局限于预聚合的分析方式。
导入日志数据或者时序数据,主要特点是旧数据不会更新,只会追加新的数据。
2.3 Key设计
明细表可以指定Duplicate Key,也可以不指定。Duplicate Key仅做数据排序和查询过滤使用,不具有唯一约束性。
自 v3.3.0 起,明细表支持使用 ORDER BY 指定排序键,可以是任意列的排列组合。如果同时使用 ORDER BY 和 DUPLICATE KEY,则 DUPLICATE KEY 无效。如果未使用 ORDER BY 和 DUPLICATE KEY,则默认选择表的前三列作为排序键。
2.4 创建语句示例
js
CREATE TABLE detail (
event_time DATETIME NOT NULL COMMENT "datetime of event",
event_type INT NOT NULL COMMENT "type of event",
user_id INT COMMENT "id of user",
device_code INT COMMENT "device code",
channel INT COMMENT "")
ORDER BY (event_time, event_type);
3. 聚合表 (Aggregate Table)
3.1 简介
在创建聚合表时,您可以定义一个聚合键,并为值列指定一个聚合函数。当多行数据具有相同的聚合键时,值列中的值将被聚合。此外,您可以单独定义排序键。如果查询中的过滤条件包含排序键,StarRocks 可以快速过滤数据,提高查询效率。
在数据分析和聚合场景中,聚合表可以减少需要处理的数据量,从而提升查询效率。
3.2 适用场景
聚合表非常适合用于数据统计和分析场景。以下是一些示例:
帮助网站或应用程序提供商分析用户在特定网站或应用程序上的流量和停留时间,以及网站或应用程序的总访问次数。
帮助广告公司分析他们为客户提供的广告的总点击量、总浏览量和消费统计数据。
帮助电子商务公司分析其年度交易数据,以识别各个季度或月份内的地理畅销商品。
上述场景中的数据查询和摄取具有以下特点:
大多数查询是聚合查询,如 SUM、MAX 和 MIN。
不需要检索原始明细数据。
历史数据不经常更新。仅追加新数据。
3.3 Key设计
- 在 CREATE TABLE 语句中,聚合键必须在其他列之前定义。
- 聚合键可以使用 AGGREGATE KEY 显式定义。AGGREGATE KEY 必须包含除值列之外的所有列,否则表创建失败。
- 如果未使用 AGGREGATE KEY 显式定义聚合键,则默认情况下,除值列之外的所有列都被视为聚合键。
- 聚合键具有唯一性约束
3.4 值列和聚合函数
值列:通过在列名后指定聚合函数来定义一个列为值列。此列通常保存需要聚合的数据。
聚合函数:用于值列的聚合函数。支持的聚合函数
- SUM、MAX、MIN、REPLACE
- HLL_UNION(仅适用于 HLL 类型)
- BITMAP_UNION(仅适用于 BITMAP)
- REPLACE_IF_NOT_NULL:这意味着只有在导入的数据为非空值时才会替换。如果为 null 值,StarRocks 将保留原始值。
通用聚合状态:可以通过指定函数名称和输入参数类型在聚合表中定义通用聚合状态,以唯一标识一个聚合函数。列类型将自动推断为聚合函数的中间状态类型。
col_name agg_func_name(parameter1_type, [parameter2_type], ...)
- col_name: 列的名称。
- agg_func_name: 需要存储其中间状态的聚合函数的名称。
- parameter_type: 聚合函数的输入参数类型。可以通过参数类型唯一标识该函数。
示例:
js
CREATE TABLE test_create_agg_table (
dt VARCHAR(10),
-- 定义通用聚合状态存储。
hll_sketch_agg ds_hll_count_distinct(varchar),
avg_agg avg(bigint),
array_agg_agg array_agg(int),
min_by_agg min_by(varchar, bigint)
)
AGGREGATE KEY(dt)
PARTITION BY (dt)
DISTRIBUTED BY HASH(dt) BUCKETS 4;
组合函数:通用聚合状态使用组合函数封装中间状态计算和流程。
3.5 创建语句示例
js
CREATE TABLE aggregate_tbl (
site_id LARGEINT NOT NULL COMMENT "id of site",
date DATE NOT NULL COMMENT "time of event",
city_code VARCHAR(20) COMMENT "city_code of user",
pv BIGINT SUM DEFAULT "0" COMMENT "total page views"
)
AGGREGATE KEY(site_id, date, city_code)
DISTRIBUTED BY HASH(site_id);
4. 更新表 (Unique Key Table)
4.1 简介
建更新表时需要定义唯一键。当多条数据具有相同的唯一键时,value 列会进行 REPLACE,查询时返回唯一键相同的一组数据中的最新数据。并且支持单独定义排序键,如果查询的过滤条件包含排序键,则 StarRocks 能够快速地过滤数据,提高查询效率。
更新表能够支撑实时和频繁更新的场景,不过目前已经逐渐被主键表代替。
4.2 适用场景
实时和频繁更新的业务场景,例如分析电商订单。在电商场景中,订单的状态经常会发生变化,每天的订单更新量可突破上亿。
4.3 Key设计
- 在建表语句中,唯一键必须定义在其他列之前。
- 唯一键需要通过 UNIQUE KEY 显式定义。
- 唯一键必须满足唯一性约束。
4.4 创建语句示例
js
CREATE TABLE orders (
create_time DATE NOT NULL COMMENT "create time of an order",
order_id BIGINT NOT NULL COMMENT "id of an order",
order_state INT COMMENT "state of an order",
total_price BIGINT COMMENT "price of an order"
)
UNIQUE KEY(create_time, order_id)
DISTRIBUTED BY HASH(order_id);
5. 表类型的选择
- 主键表:适合有高性能数据更新和查询的场景,如实时分析、用户画像等。
- 明细表:适合存储只追加记录的原始明细数据,主要用于日志分析、事件流分析等。
- 聚合表:适合频繁的聚合计算场景,减少聚合查询的延迟,如数据统计、实时分析、报表查询等
- 更新表:适合存储需要频繁更新的唯一数据,如用户信息等。
更新表和聚合表整体上采用了 Merge-On-Read 的策略。虽然写入时处理简单高效,但是读取时需要在线 Merge 多个版本的数据文件。并且由于 Merge 算子的存在,谓词和索引无法下推至底层数据,会严重影响查询性能。
主键表采用了 Delete+Insert 策略,借助主键索引配合 DelVector 的方式实现,保证在查询时只需要读取具有相同主键值的数据中的最新数据。如此可以避免 Merge 多个版本的数据文件,并且谓词和索引可以下推到底层数据,所以可以极大提升查询性能。相对于 Merge-On-Read 策略的更新表,主键表的查询性能能够提升 3~10 倍。
表类型选择原则:
1)看业务需求:是否需要聚合数据?是否需要明细数据?是否需要最新状态数据?
2)看查询性能需求:是否接受延迟?是否要求强一致性?
3)看写入性能需求:是否高并发写入?
综合考虑业务需求和读写性能要求来选择表类型。