文章目录
-
- 前言
-
- 2.3.3 管理分区
-
- 2.3.3.1 增加分区
- 2.3.3.2 删除分区
- 2.3.3.3 恢复分区
- 2.3.3.4 查看分区
- 2.3.4 设置分桶
-
- 2.3.4.1 随机分桶(自 v3.1)
- 2.3.4.2 哈希分桶
-
- 2.3.4.2.1 优点
- 2.3.4.2.2 如何选择分桶键
- 2.3.4.2.3 注意事项
- 2.3.4.3 确定分桶数量
- 2.3.5 最佳实践
- 2.4 数据压缩
-
- 2.4.1 选择数据压缩算法
- 2.4.2 设置数据压缩算法
- 2.5 排序键和前缀索引
-
- 2.5.1 排序原理
- 2.5.2 排序效果
- 2.5.3 选择排序列
-
- 导入导出数据
前言
本文为Flink-StarRocks详解后续章节:主要详解StarRocks分区分桶管理,数据压缩,
由于篇幅过长,后续接着下面进行详解:
StarRocks查询数据湖
实现即席查询案例
2.3.3 管理分区
2.3.3.1 增加分区
对于 Range 分区和 List 分区,可以手动增加新的分区,用于存储新的数据,而表达式分区可以实现导入新数据时自动创建分区,无需手动新增分区。 新增分区的默认分桶数量和原分区相同。也可以根据新分区的数据规模调整分桶数量。
如下示例中,在 site_access5 表添加新的分区,用于存储新月份的数据:
ALTER TABLE test.site_access5
ADD PARTITION p4 VALUES LESS THAN ("2020-04-30")
DISTRIBUTED BY HASH(site_id);
2.3.3.2 删除分区
执行如下语句,删除 site_access5 表中分区 p1 及数据:
说明:分区中的数据不会立即删除,会在 Trash 中保留一段时间(默认为一天)。如果误删分区,可以通过 RECOVER 命令恢复分区及数据。
ALTER TABLE test.site_access5
DROP PARTITION p1;
2.3.3.3 恢复分区
执行如下语句,恢复 site_access5 表中分区 p1 及数据:
RECOVER PARTITION p1 FROM test.site_access5;
2.3.3.4 查看分区
执行如下语句,查看 site_access5 表中分区情况:
SHOW PARTITIONS FROM test.site_access5;
2.3.4 设置分桶
2.3.4.1 随机分桶(自 v3.1)
对每个分区的数据,StarRocks 将数据随机地分布在所有分桶中,适用于数据量不大,对查询性能要求不高的场景。如果不设置分桶方式,则默认由 StarRocks 使用随机分桶,并且自动设置分桶数量。
不过值得注意的是,如果查询海量数据且查询时经常使用一些列会作为条件列,随机分桶提供的查询性能可能不够理想。在该场景下建议使用哈希分桶,当查询时经常使用这些列作为条件列时,只需要扫描和计算查询命中的少量分桶,则可以显著提高查询性能。
使用限制
仅支持明细模型表。
不支持指定 Colocation Group。
不支持 Spark Load。
如下建表示例中,没有使用 DISTRIBUTED BY xxx 语句,即表示默认由 StarRocks 使用随机分桶,并且由 StarRocks 自动设置分桶数量。
CREATE TABLE test.site_access9(
event_day DATE,
site_id INT DEFAULT '10',
pv BIGINT DEFAULT '0' ,
city_code VARCHAR(100),
user_name VARCHAR(32) DEFAULT ''
)
DUPLICATE KEY(event_day,site_id,pv)
PROPERTIES (
"replication_num" = "1"
);
当然,如果比较熟悉 StarRocks 的分桶机制,使用随机分桶建表时,也可以手动设置分桶数量。
CREATE TABLE test.site_access10(
event_day DATE,
site_id INT DEFAULT '10',
pv BIGINT DEFAULT '0' ,
city_code VARCHAR(100),
user_name VARCHAR(32) DEFAULT ''
)
DUPLICATE KEY(event_day,site_id,pv)
DISTRIBUTED BY RANDOM BUCKETS 8 -- 手动设置分桶数量为 8
PROPERTIES (
"replication_num" = "1"
);
2.3.4.2 哈希分桶
对每个分区的数据,StarRocks 会根据分桶键和分桶数量进行哈希分桶。在哈希分桶中,使用特定的列值作为输入,通过哈希函数计算出一个哈希值,然后将数据根据该哈希值分配到相应的桶中。
2.3.4.2.1 优点
提高查询性能。相同分桶键值的行会被分配到一个分桶中,在查询时能减少扫描数据量。
均匀分布数据。通过选取较高基数(唯一值的数量较多)的列作为分桶键,能更均匀的分布数据到每一个分桶中。
2.3.4.2.2 如何选择分桶键
假设存在列同时满足高基数和经常作为查询条件,则建议选择其为分桶键,进行哈希分桶。 如果不存在这些同时满足两个条件的列,则需要根据查询进行判断。
如果查询比较复杂,则建议选择高基数的列为分桶键,保证数据在各个分桶中尽量均衡,提高集群资源利用率。
如果查询比较简单,则建议选择经常作为查询条件的列为分桶键,提高查询效率。
并且,如果数据倾斜情况严重,还可以使用多个列作为数据的分桶键,但是建议不超过 3 个列。
2.3.4.2.3 注意事项
建表时,如果使用哈希分桶,则必须指定分桶键。
组成分桶键的列仅支持整型、DECIMAL、DATE/DATETIME、CHAR/VARCHAR/STRING 数据类型。
分桶键指定后不支持修改。
如下示例中,site_access 表采用 site_id 作为分桶键,其原因在于 site_id 为高基数列。此外,针对 site_access 表的查询请求,基本上都以站点作为查询过滤条件,采用 site_id 作为分桶键,还可以在查询时裁剪掉大量无关分桶。
CREATE TABLE test.site_access11(
event_day DATE,
site_id INT DEFAULT '10',
city_code VARCHAR(100),
user_name VARCHAR(32) DEFAULT '',
pv BIGINT SUM DEFAULT '0'
)
AGGREGATE KEY(event_day, site_id, city_code, user_name)
PARTITION BY RANGE(event_day) (
PARTITION p1 VALUES LESS THAN ("2020-01-31"),
PARTITION p2 VALUES LESS THAN ("2020-02-29"),
PARTITION p3 VALUES LESS THAN ("2020-03-31")
)
DISTRIBUTED BY HASH(site_id)
PROPERTIES (
"replication_num" = "1"
);
如下查询中,假设每个分区有 10 个分桶,则其中 9 个分桶被裁减,因而系统只需要扫描 site_access 表中 1/10 的数据:
select sum(pv)
from test.site_access11
where site_id = 54321;
但是如果 site_id 分布十分不均匀,大量的访问数据是关于少数网站的(幂律分布,二八规则),那么采用上述分桶方式会造成数据分布出现严重的倾斜,进而导致系统局部的性能瓶颈。此时,需要适当调整分桶的字段,以将数据打散,避免性能问题。例如,可以采用 site_id、city_code 组合作为分桶键,将数据划分得更加均匀。相关建表语句如下:
CREATE TABLE test.site_access12
(
site_id INT DEFAULT '10',
city_code SMALLINT,
user_name VARCHAR(32) DEFAULT '',
pv BIGINT SUM DEFAULT '0'
)
AGGREGATE KEY(site_id, city_code, user_name)
DISTRIBUTED BY HASH(site_id,city_code)
PROPERTIES (
"replication_num" = "1"
);
在实际使用中,可以依据自身的业务特点选择以上两种分桶方式。采用 site_id 的分桶方式对于短查询十分有利,能够减少节点之间的数据交换,提高集群整体性能;采用 site_id、city_code 的组合分桶方式对于长查询有利,能够利用分布式集群的整体并发性能。
说明:
短查询是指扫描数据量不大、单机就能完成扫描的查询。
长查询是指扫描数据量大、多机并行扫描能显著提升性能的查询。
2.3.4.3 确定分桶数量
在 StarRocks 中,分桶是实际物理文件组织的单元。
建表时如何设置分桶数量
方式一:自动设置分桶数量
自 2.5.7 版本起, StarRocks 支持根据机器资源和数据量自动设置分区的分桶数量。
建表示例:
CREATE TABLE test.site_access13(
site_id INT DEFAULT '10',
city_code SMALLINT,
user_name VARCHAR(32) DEFAULT '',
pv BIGINT SUM DEFAULT '0'
)
AGGREGATE KEY(site_id, city_code, user_name)
DISTRIBUTED BY HASH(site_id,city_code)
-- 无需手动设置分桶数量
PROPERTIES (
"replication_num" = "1"
);
如果需要开启该功能,则需要确保 FE 动态参数 enable_auto_tablet_distribution 为 true。 建表后,可以执行 SHOW PARTITIONS 来查看 StarRock 为分区自动设置的分桶数量。
SHOW PARTITIONS FROM test.site_access13;
方式二:手动设置分桶数量
自 2.4 版本起,StarRocks 提供了自适应的 Tablet 并行扫描能力,即一个查询中涉及到的任意一个 Tablet 可能是由多个线程并行地分段扫描,减少了 Tablet 数量对查询能力的限制,从而可以简化对分桶数量的设置。简化后,确定分桶数量方式可以是:首先预估每个分区的数据量,然后按照每 10 GB 原始数据一个 Tablet 计算,从而确定分桶数量。
如果需要开启并行扫描 Tablet,则需要确保系统变量 enable_tablet_internal_parallel 全局生效 SET GLOBAL enable_tablet_internal_parallel = true;。
CREATE TABLE test.site_access14(
site_id INT DEFAULT '10',
city_code SMALLINT,
user_name VARCHAR(32) DEFAULT '',
pv BIGINT SUM DEFAULT '0')
AGGREGATE KEY(site_id, city_code, user_name)
DISTRIBUTED BY HASH(site_id,city_code) BUCKETS 30
-- 假设导入一个分区的原始数据量为 300 GB,则按照每 10 GB 原始数据一个 Tablet,则分桶数量可以设置为 30。
PROPERTIES (
"replication_num" = "1"
);
新增分区时如何设置分桶数量
方式一:自动设置分桶数量(推荐)
自 2.5.7 版本起, StarRocks 支持根据机器资源和数据量自动设置分区的分桶数量。
如果需要启用该功能,则需要确保 FE 动态参数 enable_auto_tablet_distribution 保持默认值 true。如果需要关闭该功能,则可以执行ADMIN SET FRONTEND CONFIG ("enable_auto_tablet_distribution" = "false");并且新增分区的时候未指定分桶数量,则新增分区的分桶数量会继承建表时的分桶数量。 新增分区后,可以执行 SHOW PARTITIONS 来查看 StarRocks 为新增分区自动设置的分桶数量。
方式二:手动设置分桶数量
新增分区的时候,也可以手动指定分桶数量。新增分区的分桶数量的计算方式可以参考如上建表时手动设置分桶数量。
-- 手动创建分区
ALTER TABLE <table_name>
ADD PARTITION <partition_name>
[DISTRIBUTED BY HASH (k1[,k2 ...]) [BUCKETS num]];
-- 手动设置动态分区的默认分桶数量
ALTER TABLE <table_name>
SET ("dynamic_partition.buckets"="xxx");
注意:
不支持修改已创建分区的分桶数量。
2.3.5 最佳实践
对于 StarRocks 而言,分区和分桶的选择是非常关键的。在建表时选择合理的分区键和分桶键,可以有效提高集群整体性能。因此建议在选择分区键和分桶键时,根据业务情况进行调整。
数据倾斜
如果业务场景中单独采用倾斜度大的列做分桶,很大程度会导致访问数据倾斜,那么建议采用多列组合的方式进行数据分桶。
高并发
分区和分桶应该尽量覆盖查询语句所带的条件,这样可以有效减少扫描数据,提高并发。
高吞吐
尽量把数据打散,让集群以更高的并发扫描数据,完成相应计算。
元数据管理
控制Tablet数量,因为Tablet 过多会增加 FE/BE 的元数据管理和调度的资源消耗。
2.4 数据压缩
StarRocks 支持对表和索引数据进行压缩 (compression)。数据压缩不仅有助于节省存储空间,还可以提高 I/O 密集型任务的性能。
注意:压缩和解压缩数据需要额外的 CPU 资源。
2.4.1 选择数据压缩算法
StarRocks 支持四种数据压缩算法:LZ4、Zstandard(或 zstd)、zlib 和 Snappy。 这些数据压缩算法在压缩率和压缩/解压缩性能上有所不同。 通常来说,这些算法的压缩率排名如下:zlib > Zstandard > LZ4 > Snappy。其中,zlib 拥有较高的压缩比。但由于数据高度压缩,使用 zlib 算法的表,其导入和查询性能会受到一定程度的影响。而 LZ4 和 Zstandard 算法具有较为均衡的压缩比和解压缩性能。可以根据自身业务场景在这些压缩算法中进行选择,以满足对存储或性能的需求。如果对存储空间占用没有特殊需求,建议使用 LZ4 或 Zstandard 算法。
说明
不同的数据类型也会影响算法的压缩率。
2.4.2 设置数据压缩算法
只能在创建表时为其设置数据压缩算法,且后续无法修改。
以下示例基于 Zstandard 算法创建表 data_compression。
CREATE TABLE test.data_compression(
`id` INT(11) NOT NULL COMMENT "",
`name` CHAR(200) NULL COMMENT ""
)
UNIQUE KEY(`id`)
DISTRIBUTED BY HASH(`id`)
PROPERTIES (
"compression" = "ZSTD",
"replication_num" = "1"
);
说明
如不指定数据压缩算法,StarRocks 默认使用 LZ4。
可以通过 SHOW CREATE TABLE 命令查看指定表采用的压缩算法。
2.5 排序键和前缀索引
在建表时,可以指定一个或多个列构成排序键 (Sort Key)。表中的行会根据排序键进行排序以后再落入磁盘存储。查询数据时,可以使用排序列指定过滤条件,StarRocks 不需要扫描全表即可快速找到需要处理的数据,降低搜索的复杂度,从而加速查询。
同时,为减少内存开销,StarRocks 在排序键的基础上又引入了前缀索引 (Prefix Index)。前缀索引是一种稀疏索引。表中每 1024 行数据构成一个逻辑数据块 (Data Block)。每个逻辑数据块在前缀索引表中存储一个索引项,索引项的长度不超过 36 字节,其内容为数据块中第一行数据的排序列组成的前缀,在查找前缀索引表时可以帮助确定该行数据所在逻辑数据块的起始行号。前缀索引的大小会比数据量少 1024 倍,因此会全量缓存在内存中,在实际查找的过程中可以有效加速查询。
2.5.1 排序原理
在明细模型中,排序列就是通过 DUPLICATE KEY 关键字指定的列。
在聚合模型中,排序列就是通过 AGGREGATE KEY 关键字指定的列。
在更新模型中,排序列就是通过 UNIQUE KEY 关键字指定的列。
自 3.0 版本起,主键模型解耦了主键列和排序列,排序列通过 ORDER BY 关键字指定,主键列通过 PRIMARY KEY 关键字指定。
主键模型建表示例:
CREATE TABLE test.site_access_primary
(
site_id INT DEFAULT '10',
city_code SMALLINT,
user_id INT,
pv BIGINT DEFAULT '0'
)
PRIMARY KEY(site_id)
DISTRIBUTED BY HASH(site_id)
ORDER BY(site_id,city_code)
PROPERTIES (
"replication_num" = "1"
);
在明细模型、聚合模型、更新模型中定义排序列时,需要注意以下几点:
排序列必须从定义的第一列开始、并且是连续的。
在定义各列时,计划作为排序列的列必须定义在其他普通列之前。
排序列的顺序必须与表定义的列顺序一致。
例如,建表语句中声明要创建 site_id、city_code、user_id 和 pv 四列。这种情况下,正确的排序列组合和错误的排序列组合举例如下:
正确的排序列
site_id 和 city_code
site_id、city_code 和 user_id
错误的排序列
city_code 和 site_id
city_code 和 user_id
site_id、city_code 和 pv
下面通过示例来说明如何创建使用各个数据模型的表。
2.5.2 排序效果
以上述建表语句为例,排序效果可以分为三种情况:
如果查询条件只包含 site_id 和 city_code 两列,如下所示,则可以大幅减少查询过程中需要扫描的数据行:
select sum(pv) from test.site_access_duplicate where site_id = 123 and city_code = 2;
如果查询条件只包含 site_id 一列,如下所示,可以定位到只包含 site_id 的数据行:
select sum(pv) from test.site_access_duplicate where site_id = 123;
如果查询条件只包含 city_code 一列,如下所示,则需要扫描所有数据行,排序效果大打折扣:
select sum(pv) from test.site_access_duplicate where city_code = 2;
说明
这种情况下,排序列无法实现应有的排序效果。
在第一种情况下,为了定位到数据行的位置,需进行二分查找,以找到指定区间。如果数据行非常多,直接对 site_id 和 city_code 两列进行二分查找,需要把两列数据都加载到内存中,这样会消耗大量的内存空间。这时候可以使用前缀索引来减少缓存的数据量、并有效加速查询。
另外,在实际业务场景中,如果指定的排序列非常多,也会占用大量内存。为了避免这种情况,StarRocks 对前缀索引做了如下限制:
前缀索引项的内容只能由数据块中第一行的排序列的前缀组成。
前缀索引列的数量不能超过 3。
前缀索引项的长度不能超过 36 字节。
前缀索引中不能包含 FLOAT 或 DOUBLE 类型的列。
前缀索引中 VARCHAR 类型的列只能出现一次,并且处在末尾位置。
当前缀索引的末尾列是 CHAR 或 VARCHAR 类型时,前缀索引项的长度不会超过 36 字节。
2.5.3 选择排序列
这里以 site_access_duplicate 表为例,介绍如何选择排序列。
经常作为查询条件的列,建议选为排序列。
当排序键涉及多个列的时候,建议把区分度高、且经常查询的列放在前面。区分度高的列是指取值个数较多、且持续增加的列。例如,在上述 site_access_duplicate 表中,因为城市的数目是固定的,所以 city_code 列中取值的个数是固定的,而 site_id 列中取值的个数要比 city_code 列中取值的个数大得多、并且还会不断地增加,所以 site_id 列的区分度就比 city_code 列要高不少。
排序键不应该包含过多的列。选择很多排序列并不有助于提升查询性能,而且会增大排序的开销,进而增加数据导入的开销。
综上所述,在为 site_access_duplicate 表选择排序列时,需要注意以下三点:
如果需要经常按 site_id 列加 city_code 列的组合进行查询,建议选择 site_id 列作为排序键的第一列。
如果需要经常按 city_code 列进行查询、偶尔按 site_id 列加 city_code 列的组合进行查询,建议选择 city_code 列作为排序键的第一列。
极端情况下,如果按 site_id 列加 city_code 列组合查询所占的比例与按 city_code 列单独查询所占的比例不相上下,可以创建一个以 city_code 列为第一列的 Rollup 表。Rollup 表会为 city_code 列再建一个排序索引 (Sort Index)。
3. 导入导出数据
数据导入是指将原始数据按照业务需求进行清洗、转换、并加载到 StarRocks 中的过程,从而可以在 StarRocks 系统中进行极速统一的数据分析。
具体详见:
https://docs.starrocks.io/zh/docs/loading/Loading_intro/
另外,StarRocks也支持导出数据,可以通过 EXPORT 语句把 StarRocks 集群中指定表或分区上的数据以 CSV 的格式导出到外部存储系统。
具体详见:https://docs.starrocks.io/zh/docs/unloading/Export/