明细模型:表中会存在主键重复的数据行,并且与导入的数据是完全对应的。您可以召回所导入的全部历史数据。
聚合模型:表中不存在主键重复的数据行,主键满足唯一性约束。导入的数据中主键重复的数据行聚合为一行,即具有相同主键的指标列,会通过聚合函数进行聚合。您只能召回导入的全部历史数据的聚合结果,但是无法召回历史明细数据。
主键模型和更新模型:表中不存在主键重复的数据行,主键满足唯一性约束。最新导入的数据行,替换掉其他主键重复的数据行。这两种模型可以视为聚合模型的特殊情况,相当于在聚合模型中,为表的指标列指定聚合函数为 REPLACE,REPLACE 函数返回主键相同的一组数据中的最新数据。
主键模型支持分别定义主键和排序键。数据导入至主键模型的表时先按照排序键排序后存储。查询时返回主键相同的一组数据中的最新数据。相对于更新模型,主键模型在查询时不需要执行聚合操作,并且支持谓词和索引下推,能够在支持实时和频繁更新等场景的同时,提供高效查询。支持增删更新。
适用场景:
主键模型适用于实时和频繁更新的场景,例如:
实时对接事务型数据至 StarRocks。事务型数据库中,除了插入数据外,一般还会涉及较多更新和删除数据的操作,因此事务型数据库的数据同步至 StarRocks 时,建议使用主键模型。通过 Flink-CDC 等工具直接对接 TP 的 Binlog,实时同步增删改的数据至主键模型,可以简化数据同步流程,并且相对于 Merge-On-Read 策略的更新模型,查询性能能够提升 3~10 倍。
利用部分列更新轻松实现多流 JOIN。在用户画像等分析场景中,一般会采用大宽表方式来提升多维分析的性能,同时简化数据分析师的使用模型。而这种场景中的上游数据,往往可能来自于多个不同业务(比如来自购物消费业务、快递业务、银行业务等)或系统(比如计算用户不同标签属性的机器学习系统),主键模型的部分列更新功能就很好地满足这种需求,不同业务直接各自按需更新与业务相关的列即可,并且继续享受主键模型的实时同步增删改数据及高效的查询性能。
StarRocks 通过 delete + insert 的方式支持 OLAP 场景的实时 update 能力,在过去的2.0系列版本里,主要围绕功能、性能持续进行提升。在功能方面,支持了 partial update、conditional update 的能力,通过数据列的字段来标识数据操作;在性能方面,从全内存的主键索引升级为持久化的主键索引,解决主键模型内存占用的问题。 在 3.0 版本,StarRocks 更进一步,提供了 Update 语法的支持,包括跨表更新、CTE 等语法的能力,使得 StarRocks 的更新使用起来更加简单。
注意事项:
如果开启持久化索引,主键模型大大降低了主键索引对内存的占用。因为导入时少部分主键索引存在内存中,大部分主键索引存在磁盘中。单条主键编码后的最大长度为 128 字节。
如果不开启持久化索引,主键模型适用于主键占用空间相对可控的场景,因为导入时将主键索引加载至内存中。单条主键编码后的最大长度和内存占用上限为 128 字节。 如下两个场景中,主键占用空间相对可控:
数据有冷热特征,即最近几天的热数据才经常被修改,老的冷数据很少被修改。例如,MySQL订单表实时同步到 StarRocks 中提供分析查询。其中,数据按天分区,对订单的修改集中在最近几天新创建的订单,老的订单完成后就不再更新,因此导入时其主键索引就不会加载,也就不会占用内存,内存中仅会加载最近几天的索引。
大宽表(数百到数千列)。主键只占整个数据的很小一部分,其内存开销比较低。比如用户状态和画像表,虽然列非常多,但总的用户数不大(千万至亿级别),主键索引内存占用相对可控。
建表语句:
create table users (
user_id bigint NOT NULL,
name string NOT NULL,
email string NULL,
address string NULL,
age tinyint NULL,
sex tinyint NULL,
last_active datetime,
property0 tinyint NOT NULL,
property1 tinyint NOT NULL,
property2 tinyint NOT NULL,
property3 tinyint NOT NULL
) PRIMARY KEY (user_id)
DISTRIBUTED BY HASH(user_id)
ORDER BY(`address`,`last_active`)
PROPERTIES (
"replication_num" = "3",
"enable_persistent_index" = "true"
);
使用说明:
主键相关的说明:
在建表语句中,主键必须定义在其他列之前。
主键通过 PRIMARY KEY 定义。
主键必须满足唯一性约束,且列的值不会修改。本示例中主键为 dt、order_id。
主键支持以下数据类型:BOOLEAN、TINYINT、SMALLINT、INT、BIGINT、LARGEINT、DATE、DATETIME、VARCHAR/STRING。并且不允许为 NULL。
分区列和分桶列必须在主键中
enable_persistent_index:是否持久化主键索引,同时使用磁盘和内存存储主键索引,避免主键索引占用过大内存空间。通常情况下,持久化主键索引后,主键索引所占内存为之前的 1/10。您可以在建表时,在PROPERTIES中配置该参数,取值范围为 true 或者 false(默认值)。
## 自 2.3.0 版本起,StarRocks 支持配置该参数。
## 配置参数:storage_root_path/enable_strict_storage_medium_check
## 建表参数:"storage_medium" = "SSD","storage_cooldown_time" = "2023-01-01 23:59:59"
## 如果磁盘为固态硬盘 SSD,则建议设置为 true。如果磁盘为机械硬盘 HDD,并且导入频率不高,则也可以设置为 true。
如果未开启持久化索引,导入时主键索引存在内存中,可能会导致占用内存较多。因此建议您遵循如下建议:
合理设置主键的列数和长度。建议主键为占用内存空间较少的数据类型,例如 INT、BIGINT 等,暂时不建议为 VARCHAR。
在建表前根据主键的数据类型和表的行数来预估主键索引占用内存空间,以避免出现内存溢出。以下示例说明主键索引占用内存空间的计算方式:
# 假设存在主键模型,主键为dt、id,数据类型为 DATE(4 个字节)、BIGINT(8 个字节)。则主键占 12 个字节。
# 假设该表的热数据有 1000 万行,存储为三个副本。
# 则内存占用的计算方式:(12 + 9(每行固定开销) ) * 1000W * 3 * 1.5(哈希表平均额外开销) = 945 M
通过 ORDER BY 关键字指定排序键,可指定为任意列的排列组合。
支持使用 ALTER TABLE 进行表结构变更,但是存在如下注意事项:
## 不支持修改主键。
## 对于排序键,支持通过 ALTER TABLE ... ORDER BY ... 重新指定排序键。不支持删除排序键,不支持修改排序键中列的数据类型。
## 不支持调整列顺序
自2.3 版本起,除了主键之外的列新增支持 BITMAP、HLL 数据类型。
创建表时,支持为除了主键之外的列创建 BITMAP、Bloom Filter 等索引。
自 2.4 版本起,支持基于主键模型的表创建异步物化视图。
明细模型是默认的建表模型。如果在建表时未指定任何模型,默认创建的是明细类型的表。 导入时,支持追加新数据,不支持修改历史数据。 如果导入两行完全相同的数据,则明细模型会将这两行数据视为两行,而不是一行。
明细模型底层使用 LSM Tree 数据结构进行存储,我们都知道 LSM Tree 是一 个写友好的数据结构,在 StarRocks 数据存储层,选择了一个两层结构的 LSM Tree,减少了 compaction 对系统的压力,也减少了 write stall 的影响
使用场景:
分析原始数据,例如原始日志、原始操作记录等。
查询方式灵活,不需要局限于预聚合的分析方式。
导入日志数据或者时序数据,主要特点是旧数据不会更新,只会追加新的数据。
建表语句:
CREATE TABLE IF NOT EXISTS 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 ""
)
DUPLICATE KEY(event_time, event_type)
DISTRIBUTED BY HASH(user_id)
PROPERTIES (
"replication_num" = "3"
);
使用说明
排序键的相关说明:
在建表语句中,排序键必须定义在其他列之前。
排序键可以通过 DUPLICATE KEY 显式定义。本示例中排序键为 event_time 和 event_type。
## 如果未指定,则默认选择表的前三列作为排序键。
明细模型中的排序键可以为部分或全部维度列。
建表时,支持为指标列创建 BITMAP、Bloom Filter 等索引。
建表时,支持定义排序键和指标列,并为指标列指定聚合函数。当多条数据具有相同的排序键时,指标列会进行聚合。在分析统计和汇总数据时,聚合模型能够减少查询时所需要处理的数据,提升查询效率。
适用场景:
适用于分析统计和汇总数据。比如:
通过分析网站或 APP 的访问流量,统计用户的访问总时长、访问总次数。
广告厂商为广告主提供的广告点击总量、展示总量、消费统计等。
通过分析电商的全年交易数据,获得指定季度或者月份中,各类消费人群的爆款商品。
在这些场景中,数据查询和导入,具有以下特点:
多为汇总类查询,比如 SUM、MAX、MIN等类型的查询。
不需要查询原始的明细数据。
旧数据更新不频繁,只会追加新的数据
建表语句
CREATE TABLE IF NOT EXISTS example_db.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)
PROPERTIES (
"replication_num" = "3"
);
使用说明:
排序键的相关说明:
在建表语句中,排序键必须定义在其他列之前。
排序键可以通过 AGGREGATE KEY 显式定义。
# 如果 AGGREGATE KEY 未包含全部维度列(除指标列之外的列),则建表会失败。
# 如果不通过 AGGREGATE KEY 显示定义排序键,则默认除指标列之外的列均为排序键。
排序键必须满足唯一性约束,必须包含全部维度列,并且列的值不会更新。
指标列:通过在列名后指定聚合函数,定义该列为指标列。一般为需要汇总统计的数据。
聚合函数:指标列使用的聚合函数。聚合模型支持的聚合函数,请参见 CREATE TABLE。
查询时,排序键在多版聚合之前就能进行过滤,而指标列的过滤在多版本聚合之后。因此建议将频繁使用的过滤字段作为排序键,在聚合前就能过滤数据,从而提升查询性能。
建表时,不支持为指标列创建 BITMAP、Bloom Filter 等索引。建表时必须使用 DISTRIBUTED BY HASH 子句指定分桶键。
建表时,支持定义主键和指标列,查询时返回主键相同的一组数据中的最新数据。相对于明细模型,更新模型简化了数据导入流程,能够更好地支撑实时和频繁更新的场景。
使用场景:
实时和频繁更新的业务场景,例如分析电商订单。在电商场景中,订单的状态经常会发生变化,每天的订单更新量可突破上亿。
建表语法:
CREATE TABLE IF NOT EXISTS 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)
PROPERTIES (
"replication_num" = "3"
);
使用说明
主键的相关说明:
在建表语句中,主键必须定义在其他列之前。
主键通过 UNIQUE KEY 定义。
主键必须满足唯一性约束,且列的值不会修改。
设置合理的主键。
# 查询时,主键在聚合之前就能进行过滤,而指标列的过滤通常在多版本聚合之后,因此建议将频繁使用的过滤字段作为主键,在聚合前就能过滤数据,从而提升查询性能。
# 聚合过程中会比较所有主键,因此需要避免设置过多的主键,以免降低查询性能。如果某个列只是偶尔会作为查询中的过滤条件,则不建议放在主键中。
建表时,不支持为指标列创建 BITMAP、Bloom Filter 等索引。
本文由 mdnice 多平台发布