文章目录
03_StarRocks
(一)StarRocks简介
1、什么是StarRocks【理解】
1)概述
StarRocks 是新一代极速全场景 MPP (Massively Parallel Processing) 数据库。
MPP,即大规模并行处理。MPP是将任务并行的分散到多个服务器和节点上,在每个节点上计算完成后,将各自部分的结果汇总到一起得到最终的结果(跟Hadoop相似)。具有可伸缩性强、高可用、高性能等优势。
特点:
(1)MPP、向量化引擎、CBO优化器、物化视图,使查询速度遥遥领先
(2)支持实时数据分析
(3)StarRocks兼容MySQL协议,支持标准SQL语法
2)适用场景
StarRocks 可以满足企业级用户的多种分析需求,包括 OLAP多维分析、实时数据仓库、高并发查询、统一分析(联邦查询)。
2、系统架构【理解】
1)系统架构图
2)数据管理
StarRocks 使用列式存储,采用分区分桶机制进行数据管理。
Tablet 是 StarRocks 中最小的数据管理单元。多个分桶可以增加数据读写的效率,扩展系统支持高并发的能力。
StarRocks可以做到无需停止服务,自动完成节点的增减
每个 Tablet 都会以多副本 (replica) 的形式存储在不同的 BE 节点中。多副本能够保证数据存储的高可靠以及服务的高可用【在具体工作中,一般保留2副本即可】
3、使用【熟悉】
启动:
cd /export/server/starrocks
./fe/bin/start_fe.sh --daemon
cd /export/server/starrocks
./be/bin/start_be.sh --daemon
停止:
cd /export/server/starrocks
sh ./fe/bin/stop_fe.sh --daemon
cd /export/server/starrocks
sh ./be/bin/stop_be.sh --daemon
web访问:
Starrocks-WebUI登入:192.168.88.161:8130
Starrocks-WebUI登入:用户名:root 密码:123456
客户端访问:
mysql -h node1 -P9030 -uroot -p
123456
可以使用DataGrip等进行连接:
(二)表设计
4、StarRocks表设计【理解】
1)列式存储
StarRocks 中的表由行和列构成。
在 StarRocks 中,表数据按列存储。
在 StarRocks 中,一张表的列可以分为维度列(也称为 Key 列)和指标列(也称为 Value 列)。
2)索引
StarRocks 通过前缀索引 (Prefix Index) 和列级索引,能够快速找到目标行所在数据块的起始行号。
StarRocks 表设计原理如下图所示。
通过某行数据的维度列所构成的前缀查找该行数据的过程包含以下五个步骤:
- 先查找前缀索引表,获得逻辑数据块的起始行号。
- 查找维度列的行号索引,定位到维度列的数据块。
- 读取数据块。
- 解压、解码数据块。
- 从数据块中找到维度列前缀对应的数据项。
3)加速处理
- 聚合模型可以预先聚合value列
- 使用分区分桶切分数据
- 使用物化视图构建索引,加快查询效率
- 多种列级索引支持,加快查询
5、数据模型【掌握】
5-1 明细模型
明细模型是默认的建表模型,可以保留所有明细数据。
1)适用场景
- 适用于存储原始数据
- 查询方式灵活
- 数据不更新,只有追加
2)创建表
使用DUPLICATE KEY来指定排序键。
创建表时,支持定义排序键,使用DUPLICATE KEY指定。
示例:
例如,需要分析某时间范围的某一类事件的数据,则可以将事件时间(event_time)和事件类型(event_type)作为排序键。
CREATE TABLE IF NOT EXISTS test.detail (
event_time DATETIME NOT NULL COMMENT "事件时间",
event_type INT NOT NULL COMMENT "事件类型",
user_id INT COMMENT "用户ID",
device_code INT COMMENT "设备编码",
channel INT COMMENT ""
)
DUPLICATE KEY(event_time, event_type)
DISTRIBUTED BY HASH(user_id)
PROPERTIES (
"replication_num" = "1"
);
注意
建表时必须使用 DISTRIBUTED BY HASH 子句指定分桶键,否则建表失败。
自 2.5.7 版本起,StarRocks 支持在建表和新增分区时自动设置分桶数量 (BUCKETS),无需手动设置分桶数量。
3)使用说明
- 排序键的相关说明:
- 在建表语句中,排序键必须定义在其他列之前。
- 排序键可以通过 DUPLICATE KEY 显式定义。本示例中排序键为 event_time 和 event_type。如果未指定,则默认选择表的前三列作为排序键。
- 明细模型中的排序键可以为部分或全部维度列。
5-2 聚合模型
==建表时,支持定义排序键和指标列,并为指标列指定聚合函数。当多条数据具有相同的排序键时,指标列会进行聚合。==在分析统计和汇总数据时,聚合模型能够减少查询时所需要处理的数据,提升查询效率。
1)适用场景
适用于分析统计和汇总数据。
在这些场景中,数据查询和导入,具有以下特点:
- 多为汇总类查询,比如 SUM、MAX、MIN等类型的查询。
- 不需要查询原始的明细数据。
- 旧数据更新不频繁,只会追加新的数据。
2)原理
从数据导入至数据查询阶段,聚合模型内部同一排序键的数据会多次聚合,最终返回合并后的数据。
例如,导入如下数据至聚合模型中,排序键为 Date、Country:
Date | Country | PV |
---|---|---|
2020.05.01 | CHN | 1 |
2020.05.01 | CHN | 2 |
2020.05.01 | USA | 3 |
2020.05.01 | USA | 4 |
在聚合模型中,以上四条数据会聚合为两条数据。这样在后续查询处理的时候,处理的数据量就会显著降低。
Date | Country | PV |
---|---|---|
2020.05.01 | CHN | 3 |
2020.05.01 | USA | 7 |
3)创建表
例如需要分析某一段时间内,来自不同城市的用户,访问不同网页的总次数。则可以将网页地址 site_id、日期 date 和城市代码 city_code 作为排序键,将访问次数 pv 作为指标列,并为指标列 pv 指定聚合函数为 SUM。
在该业务场景下,建表语句如下:
CREATE TABLE IF NOT EXISTS test.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" = "1"
);
4)使用说明
排序键的相关说明:
在建表语句中,排序键必须定义在其他列之前。
排序键可以通过 AGGREGATE KEY 显式定义。
如果 AGGREGATE KEY 未包含全部维度列(除指标列之外的列),则建表会失败。
如果不通过 AGGREGATE KEY 显示定义排序键,则默认除指标列之外的列均为排序键。
排序键必须满足唯一性约束,必须包含全部维度列,并且列的值不会更新。
建议将频繁使用的过滤字段作为排序键
指标列:通过在列名后指定聚合函数,定义该列为指标列。一般为需要汇总统计的数据。
聚合函数:指标列使用的聚合函数。聚合模型支持的聚合函数,请参见 CREATE TABLE。常见的有SUM、MAX、MIN、REPLACE。
5-3 更新模型
建表时,支持定义主键和指标列,查询时返回主键相同的一组数据中的最新数据。能够更好地支撑实时和频繁更新的场景。
1)适用场景
实时和频繁更新的业务场景。不需要保留明细数据,也不需要进行预聚合,只需要保留最新的数据
2)原理
更新模型可以视为聚合模型的特殊情况,指标列指定的聚合函数为 REPLACE,返回具有相同主键的一组数据中的最新数据。
3)创建表
在电商订单分析场景中,经常按照日期对订单状态进行统计分析,则可以将经常使用的过滤字段订单创建时间 create_time、订单编号 order_id 作为主键,其余列订单状态 order_state 和订单总价 total_price 作为指标列。这样既能够满足实时更新订单状态的需求,又能够在查询中进行快速过滤。
在该业务场景下,建表语句如下:
CREATE TABLE IF NOT EXISTS test.update_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" = "1"
);
4)使用说明
- 主键的相关说明:
- 在建表语句中,主键必须定义在其他列之前。
- 主键通过 UNIQUE KEY 定义。
- 主键必须满足唯一性约束,且列的值不会修改。
- 设置合理的主键。
- 可以把经常过滤的字段作为unique key
- 在聚合操作中group by里的字段作为unique key
5-4 主键模型
主键模型支持分别定义主键和排序键。数据导入至主键模型的表时先按照排序键排序后存储。查询时返回主键相同的一组数据中的最新数据。
1)适用场景
主键模型适用于实时和频繁更新的场景。
不需要保留明细数据,也不需要进行预聚合,只需要保留最新的数据
2)原理
相比于更新模型,主键模型的元数据组织、读取、写入方式完全不同,不需要执行聚合操作,并且支持谓词和索引下推,极大地提高了查询性能。
更新模型使用 Merge-On-Read策略,而主键模型使用Delete+Insert策略。
区别:主键模型适合写少读多的场景【数据写入的同时,就做Delete+Insert操作了,所以写入效率相对较低,但是提前把数据处理好了,所以读的效率更高】。
更新模型适合写多读少的场景【数据都先写进来,我定期或在你读取的时候再合并,所以写的效率高】。
3)创建表
例如,需要按地域、最近活跃时间实时分析用户情况,则可以将表示用户 ID 的 user_id 列作为主键,表示地域的 address 列和表示最近活跃时间的 last_active 列作为排序键。建表语句如下:
CREATE TABLE IF NOT EXISTS test.primary_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" = "1",
"enable_persistent_index" = "true"
);
4)使用说明
主键相关的说明:
- 在建表语句中,主键必须定义在其他列之前。
- 主键通过 PRIMARY KEY 定义。
- 主键必须满足唯一性约束,且列的值不会修改。本示例中主键为 dt、order_id。
- 主键支持以下数据类型:BOOLEAN、TINYINT、SMALLINT、INT、BIGINT、LARGEINT、DATE、DATETIME、VARCHAR/STRING。并且不允许为 NULL。
- 分区列和分桶列必须在主键中。
通过 ORDER BY 关键字指定排序键,可指定为任意列的排列组合。
注意:如果指定了排序键,就根据排序键构建前缀索引;如果没指定排序键,就根据主键构建前缀索引。
- 支持使用 ALTER TABLE 进行表结构变更,但是存在如下注意事项:
- 不支持修改主键。
- 对于排序键,支持通过 ALTER TABLE … ORDER BY … 重新指定排序键。不支持删除排序键,不支持修改排序键中列的数据类型。
- 不支持调整列顺序。
总结:
模型类型 | 应用场景 | 建表方式 | 原理 |
---|---|---|---|
明细模型 | 存储所有明细数据 | DUPLICATE KEY() | |
聚合模型 | 分析统计或汇总数据(只需要保留聚合之后的结果) | AGGREGATE KEY() | 相同key的数据进行聚合 |
更新模型 | 实时和频繁更新的业务场景,保留最新的数据==【写多读少】== | UNIQUE KEY() | Merge-on-Read的策略 |
主键模型 | 实时和频繁更新的业务场景,保留最新的数据==【写少读多】== | PRIMARY KEY() | Delete+Insert的策略 |
6、数据分布【熟悉】
6-1 StarRocks的数据分布方式
StarRocks 支持单独和组合使用数据分布方式。
**说明:**除了常见的分布方式外, StarRocks 还支持了 Random 分布,可以简化分桶设置。 |
---|
并且 StarRocks 通过设置分区 + 分桶的方式来实现数据分布。
- 第一层为分区:在一张表中,可以进行分区,支持的分区方式有表达式分区、Range 分区和 List 分区,或者不分区(即全表只有一个分区)。
- 第二层为分桶:在一个分区中,必须进行分桶。支持的分桶方式有哈希分桶和随机分桶。
数据分布方式 | 分区和分桶方式 | 说明 |
---|---|---|
Random 分布 | 随机分桶 | 一张表为一个分区,表中数据随机分布至不同分桶。该方式为默认数据分布方式。 |
Hash 分布 | 哈希分桶 | 一张表为一个分区,对表中数据的分桶键值使用哈希函数进行计算后,得出其哈希值,分布到对应分桶。 |
Range+Random 分布 | 表达式分区或者 Range 分区 + 随机分桶 | 表的数据根据分区列值所属范围,分布至对应分区。 同一分区的数据随机分布至不同分桶。 |
Range+Hash 分布 | 表达式分区或者 Range 分区 + 哈希分桶 | 表的数据根据分区列值所属范围,分布至对应分区。 对同一分区的数据的分桶键值使用哈希函数进行计算,得出其哈希值,分布到对应分桶。 |
List+Random 分布 | 表达式分区或者 List 分区 + 随机分桶 | 表的数据根据分区列值所属枚举值列表,分布至对应分区。 同一分区的数据随机分布至不同分桶。 |
List+ Hash 分布 | 表达式分区或者 List 分区+ 哈希分桶 | 表的数据根据分区列值所属枚举值列表,分布至对应分区。 对同一分区的数据的分桶键值使用哈希函数进行计算,得出其哈希值,分布到对应分桶。 |
6-2 分区
分区用于将数据划分成不同的区间。分区的主要作用是将一张表按照分区键拆分成不同的管理单元,针对每一个管理单元选择相应的存储策略,比如分桶数、冷热策略、存储介质、副本数等。
分区方式 | 适用场景 | 分区创建方式 |
---|---|---|
表达式分区(推荐) | 原称自动创建分区,适用大多数场景,并且灵活易用。适用于按照连续日期范围或者枚举值来查询和管理数据。 | 导入时自动创建 |
Range 分区 | 典型的场景是数据简单有序,并且通常按照连续日期/数值范围来查询和管理数据。再如一些特殊场景,比如历史数据需要按月划分分区,而最近数据需要按天划分分区。 | 动态、批量或者手动创建 |
List 分区 | 典型的场景是按照枚举值来查询和管理数据,并且一个分区中需要包含各分区列的多值。比如经常按照国家和城市来查询和管理数据,则可以使用该方式,选择分区列为 city,一个分区包含属于一个国家的多个城市的数据。 | 手动创建 |
注意:
如果需要自动创建分区,就使用表达式分区即可。
如果需要手动创建分区,则使用Range分区和List分区。Range分区适用于分区的字段是一个连续值,而List分区适用于分区的字段是一个枚举值。
选择分区列和分区粒度
- 一般按照时间列结合数据量进行分区
1)时间函数表达式
自 v3.0 起,StarRocks 支持表达式分区(原称自动创建分区),可以使用时间函数表达式或列表达式来创建。
(一)语法
PARTITION BY expression
...
[ PROPERTIES( 'partition_live_number' = 'xxx' ) ]
expression ::=
{ date_trunc ( <time_unit> , <partition_column> ) |
time_slice ( <partition_column> , INTERVAL <N> <time_unit> [ , boundary ] ) }
(二)参数解释
参数 | 是否必填 | 说明 |
---|---|---|
expression | 是 | 目前仅支持 date_trunc 和 time_slice 函数。并且如果使用 time_slice 函数,则可以不传入参数 boundary,因为在该场景中该参数默认且仅支持为 floor,不支持为 ceil。 |
time_unit | 是 | 分区粒度,目前仅支持为 hour、day、month 或 year,暂时不支持为 week。如果分区粒度为 hour,则仅支持分区列为 DATETIME 类型,不支持为 DATE 类型。 |
partition_column | 是 | 分区列 仅支持为日期类型(DATE 或 DATETIME),不支持为其它类型。如果使用 date_trunc 函数,则分区列支持为 DATE 或 DATETIME 类型。如果使用 time_slice 函数,则分区列仅支持为 DATETIME 类型。分区列的值支持为 NULL。 如果分区列是 DATE 类型,则范围支持为 [0000-01-01 ~ 9999-12-31]。如果分区列是 DATETIME 类型,则范围支持为 [0000-01-01 01:01:01 ~ 9999-12-31 23:59:59]。 目前仅支持指定一个分区列,不支持指定多个分区列。 |
partition_live_number | 否 | 保留最近多少数量的分区。 |
注意:StarRocks 自动创建分区数量上限默认为 4096,由 FE 配置参数 max_automatic_partition_number 决定。该参数可以防止由于误操作而创建大量分区。
(三)示例
假设经常按天查询数据,则建表时可以使用分区表达式 date_trunc() ,并且设置分区列为 event_day ,分区粒度为 day,实现导入数据时自动按照数据所属日期划分分区。将同一天的数据存储在一个分区中,利用分区裁剪可以显著提高查询效率。
CREATE TABLE test.site_express1 (
event_day DATETIME NOT NULL,
site_id INT DEFAULT '10',
city_code VARCHAR(100),
user_name VARCHAR(32) DEFAULT '',
pv BIGINT DEFAULT '0'
)
DUPLICATE KEY(event_day, site_id, city_code, user_name)
PARTITION BY date_trunc('day', event_day)
DISTRIBUTED BY HASH(event_day, site_id)
PROPERTIES (
"replication_num" = "1"
);
导入如下两行数据,则 StarRocks 会根据导入数据的日期范围自动创建两个分区 p20230226、p20230227,范围分别为 [2023-02-26 00:00:00,2023-02-27 00:00:00)、[2023-02-27 00:00:00,2023-02-28 00:00:00)。如果后续导入数据的日期属于这两个范围,则都会自动划分至对应分区。
-- 导入两行数据