PART/ 01 部署
容量规划
【建议】参考 StarRocks 集群配置推荐[1] 做容量规划
基础环境配置
【必须】参考检查环境配置 | StarRocks[2],尤其关注 swap 关闭、overcommit 设置为1、ulimit 配置合理
机器配置
FE 节点 【建议】 8C32GB 【必须】数据盘>=200GB,建议 SSD
BE 节点
【建议】CPU:内存比,1:4,生产最小配置必须是 8C32GB+
【建议】单节点磁盘容量建议 10TB,数据盘建议最大单盘 2TB,建议 SSD 或者 NVME(如果是 HDD,建议吞吐>150MB/s,IOPS>500)
【建议】集群中节点同构(机器规格一样,避免木桶效应)
部署方案
【必须】生产环境必须最小集群规模 3FE+3BE(建议 FE 和 BE 独立部署),如果混合部署,必须配置 be.conf 中的 mem_limit 为减去其他服务后剩余内存量,例如机器内存 40G,上面已经部署了 FE,理论上限会用 8G,那么配置下mem_limit=34G (40-8-2),2G 作为系统预留 【必须】生产必须 FE 高可用部署,1 Leader + 2 Follower,如果需要提高读并发,可以扩容 Observer 节点 【必须】生产必须使用负载均衡器连接集群进行读写,一般常用 Nginx、Haproxy、F5 等
PART/ 02 建模
建表规范
仅支持 UTF8 编码 不支持修改表中的列名(即将支持) VARCHAR 最大长度 1048576 KEY 列不能使用 FLOAT、DOUBLE 类型 数据目录名、数据库名、表名、视图名、用户名、角色名大小写敏感,列名和分区名大小写不敏感 主键模型中,主键长度不超过 128 字节
模型选择
如果想要保留明细,建议使用明细模型 如果有明确主键,主键非空,写少读多,非主键列要利用索引,建议使用主键模型 如果有明确主键,主键可能为空,写多读少,建议使用更新模型 如果只想保留聚合数据,建议使用聚合模型
排序列和前缀索引选择
DUPLICATE KEY、AGGREGATE KEY、UNIQUE KEY 中指定的列,StarRocks 3.0 以前版本,主键模型中排序列通过 PRIMARY KEY 指定,StarRocks 3.0 以后版本,主键模型中排序列通过 ORDER BY 指定。
经常作为查询条件的列,建议选为排序列,例如经常用 user_id 过滤,where user_id=234,可以把 user_id 放在第一列 排序列建议选择 3-5 列,过多会增大排序开销,降低导入效率 前缀索引不超过 36 字节,不能超过 3 列,遇到 VARCHAR 会截断,前缀索引中不能包含 FLOAT 或 DOUBLE 类型的列
因此可以结合实际业务查询场景,在确定 Key 列以及字段顺序时,要充分考虑前缀索引带来的优势。尽可能将经常需要查询的 Key 列字段,放置在前面,字段数据类型尽量选择 date 日期类型或者 int 等整数类型。
CREATE TABLE site_access
(
site_id BIGINT DEFAULT '10',
city_code INT,
site_name VARCHAR(50),
pv BIGINT DEFAULT '0'
)
DUPLICATE KEY(site_id,city_code,site_name)
DISTRIBUTED BY HASH(site_id);
在 site_access 表中,前缀索引为 site_id( 8 Bytes ) + city_code( 4 Bytes ) + site_name(前 24 Bytes)
如果查询条件只包含
site_id
和city_code
两列,如下所示,则可以大幅减少查询过程中需要扫描的数据行:
select sum(pv) from site_access where site_id = 123 and city_code = 2;
如果查询条件只包含
site_id
一列,如下所示,可以定位到只包含site_id
的数据行:
select sum(pv) from site_access where site_id = 123;
如果查询条件只包含
city_code
一列,如下所示,则需要扫描所有数据行,排序效果大打折扣:
select sum(pv) from site_access where city_code = 2;
如果 site_id 和 city_code 在联合查询和单独 city_code 的查询占比不相上下,可以考虑创建同步物化视图调整列顺序来达到查询性能提升。在这种情况下,我们将物化视图中的 city_code 列放置在第一列。
create materialized view site_access_city_code_mv as
select
city_code,
site_id,
site_name,
pv
from
site_access;
Bad case
CREATE TABLE site_access_bad
(
site_name VARCHAR(20),
site_id BIGINT DEFAULT '10',
city_code INT,
pv BIGINT DEFAULT '0'
)
PRIMARY KEY(site_id)
DISTRIBUTED BY HASH(site_id)
ORDER BY(site_id,city_code);
分区选择
【建议】值不会变化的时间列经常用于 WHERE 过滤,使用该列创建分区 【建议】有数据淘汰需求的场景建议选择动态分区 【必须】数据更新有明显的冷热特征的,必须创建分区,例如经常更新最近一周的数据,可以按天分区 【必须】单个分区数据量必须不超过 100GB 【必须】超过 50G 或者 5KW 的表建议创建分区 【建议】按需创建分区,不要提前创建大量空分区,避免元数据太多占用 FE 的内存 当前支持时间类型(Range 分区、表达式分区)、字符串(List 分区)、数字(Range 分区、List 分区) 默认最大支持 1024 个分区,可以通过参数调整,不过一般情况下不需要调整
分桶选择
【必须】单个桶按照 1GB 预估,原始数据按照 10GB(导入 StarRocks 后,压缩比 7:1~10:1)预估。 当按照以上策略估算出来的分桶个数小于 BE 个数的时候,最终分桶个数以 BE 个数为准,例如 6 个 BE 节点,按照 1GB 每个桶预估分桶个数为 1,最终分桶个数取 6 【必须】非分区表不要使用动态分桶,按照实际数据量估算分桶个数 【必须】如果分区表的各个分区的数据差异很大,建议不要使用动态分桶策略
【建议】如果分桶列是 WHERE 中经常用到的列,且分桶列的重复度比较低(例如用户 ID、事物 ID 等),则可以利用该列作为分桶列
【建议】当查询条件包含 city_id 和 site_id 时,若 city_id 的取值仅有几十个,简单地只使用 city_id 作为分桶可能导致某些桶数据量过大,引发数据倾斜问题。在这种情况下,可以考虑将 city_id 和 site_id 联合作为分桶字段。不过这样做的缺点是当查询条件中只包含 city_id 时,无法利用分桶进行数据裁剪。
【建议】如果没有合适的字段作为分桶字段打散数据,可以利用 Random 分桶,不过这样的话没办法利用分桶裁剪的属性
【必须】2 个或多个超过 KW 行以上的表 Join,建议使用 Colocate,具体参考 Colocate Join | StarRocks[3]
字段类型
【建议】不要使用 null 属性
索引选择
Bitmap 索引
适合基数在 10000-100000 左右的列 适合等值条件 (=) 查询或 [NOT] IN 范围查询的列 不支持为 FLOAT、DOUBLE、BOOLEAN 和 DECIMAL 类型的列创建 Bitmap 索引 城市、性别这些基数在 255 以下的列不需要创建 Bitmap 索引,因为 StarRocks 内部有低基数字典,会针对这些 case 自动创建低基数字典用于加速 明细模型和主键模型,所有列可以创建 Bitmap 索引;聚合模型和更新模型,只有 Key 列支持创建 Bitmap 索引
适合基数在 100000+ 的列,列的重复度很低
适合
in
和=
过滤条件的查询不支持为 TINYINT、FLOAT、DOUBLE 和 DECIMAL 类型的列创建 Bloom filter 索引
主键模型和明细模型中所有列都可以创建 Bloom filter 索引;聚合模型和更新模型中,只有维度列(即 Key 列)支持创建 Bloom filter 索引
PART/ 03 导入
使用建议
【必须】生产禁止使用 insert into values() 导数据 【必须】建议导入批次间隔 5s+,也就是攒批写入,尤其是实时场景 【建议】主键模型更新场景,建议开启索引落盘,磁盘强制 SSD、NVME 或者更高性能的磁盘 【建议】比较多 ETL(insert into select)的场景,建议开启 Spill 落盘功能,避免内存超过限制
数据生命周期
【建议】使用 truncate 删除数据,不要使用 delete 【必须】完整的 update 语法只能用于 3.0 版本以后的主键模型,禁止高并发 update,建议每次 update 操作需要间隔分钟以上 【必须】如果使用 delete 删除数据,需要带上 where 条件,并且禁止并发执行 delete,例如要删除 id=1,2,3,4,……1000,禁止 delete xxx from tbl1 where id=1 这样的语句执行1000条,建议 delete xxx from tbl1 where id in (1,2,3...,1000) 【必须】drop 操作默认会进入 FE 回收站,并保留 86400 秒(即 1 天),在这段时间内可以 recover 进行恢复,以防误操作。此行为受 catalog_trash_expire_second 参数控制。超过 1 天后,文件会移至 BE 的 trash 目录,默认保留 259200 秒(即 3 天)。 版本 2.5.17、3.0.9 和 3.1.6 之后版本开始,BE 的默认保留时间已调整为86400 秒( 1 天),这一设置受 trash_file_expire_time_sec 参数影响。如果需要在 drop 操作后迅速释放磁盘空间,可以适当减少 FE 和 BE 的 trash 保留时间。
PART/ 04 查询
高并发场景
【建议】尽可能利用分区分桶裁剪,具体参考上文的分区和分桶选择部分 【必须】调大客户的并发限制,可以设置为 1000,默认 100,SET PROPERTY FOR 'jack' 'max_user_connections' = '1000'; 【必须】开启 Page Cache、Query Cache
数据精度
【必须】如果需要精确结果的,强制使用 DECIMAL 类型,不要使用 FLOAT、DOUBLE 类型
SQL 查询
【必须】避免 select *,建议指定需要查询的列,例如 select col0,col1 from tb1 【必须】避免全表扫描,建议增加过滤的谓词,例如 select col0,col1 from tb1 where id=123,select col0,col1 from tb1 where dt>'2024-01-01' 【必须】为防止大量数据的一次性下载,建议强制采用分页查询。例如,使用以下分页查询语句来限制结果集中的列数和记录数:SELECT col0, col1, col2, ..., col50 FROM tb ORDER BY id LIMIT 0, 50000。这样可以有效地管理和减少单次查询返回的数据量 【必须】分页操作需要加上 order by,要不然是无序的 【建议】避免使用一些不必要的函数或者表达式
谓词中含 cast, 可以移除
过度使用函数处理表达式
-- Q1
select l_tax
from lineitem
where cast(l_shipdate as varchar) > substr('1990-01-02 12:30:31',1,10);
-- Q2
select l_tax
from lineitem
where l_shipdate > '1990-01-02';
-- Q1 bad case
select count(1)
from lineitem
where l_shipdate >= regexp_extract("TIME:1996-01-02 20:00:00", "(\\d{4}-\\d{2}-\\d{2})", 1);
-- Q2
select count(1)
from lineitem
where l_shipdate >= "1996-01-02"
-- Q1 bad case
select count(1)
from lineitem
where DATE_FORMAT(l_shipdate,'%Y-%m-%d') >= "1996-01-02"
-- Q2 good case
select count(1)
from lineitem
where l_shipdate >= "1996-01-02"
Join 【必须】关联的字段类型匹配,虽然 StarRocks 已经在内部做了隐式转换来达到最优的性能,不过建议大家使用类型一致的字段 Join,避免使用 FLOAT、DOUBLE 类型 Join,可能会导致结果不准确 【必须】关联字段建议不要使用函数或者表达式,例如 join on DATE_FORMAT(tb1.col1,'%Y-%m-%d')=DATE_FORMAT(tb2.col1,'%Y-%m-%d') 【必须】2 个或多个 KW 行以上的表 Join,推荐 Colocate Join 【建议】避免笛卡尔积 查询多个表需要指定连接条件
-- bad case
SELECT *
FROM table1, table2;
-- good case
SELECT *
FROM table1, table2 ON table1.column1 = table2.column1;
在子查询中,确保外部查询和子查询之间的列有明确的关联
-- bad case
SELECT *
FROM table1
WHERE column1 IN (SELECT column2 FROM table2);
-- good case
SELECT *
FROM table1
WHERE column1 IN (SELECT column2 FROM table2 WHERE
使用物化视图加速查询
精确去重
advertiser_view_record
,其中记录了点击日期 click_time
、广告代码 advertiser
、点击渠道 channel
以及点击用户 ID user_id
。CREATE TABLE advertiser_view_record(
click_time DATE,
advertiser VARCHAR(10),
channel VARCHAR(10),
user_id INT) distributed BY hash(click_time);
SELECT advertiser, channel, count(distinct user_id)FROM advertiser_view_record
GROUP BY advertiser, channel;
CREATE MATERIALIZED VIEW advertiser_uv AS SELECT advertiser, channel, bitmap_union(to_bitmap(user_id))FROM advertiser_view_record
GROUP BY advertiser, channel;
count(distinct user_id)
会被自动改写为 bitmap_union_count (to_bitmap(user_id))
以便查询命中物化视图。异步物化视图最多支持 3 层嵌套
利用 Cache 加速查询
【建议】Page Cache:建议开启,可以加速数据扫描场景,如果内存有冗余,可以尽可能调大限制,默认是 mem_limit*20% 【建议】Query Cache,建议开启,可以加速单表或多表 Join 的聚合场景 查询中不能包含 rand
、random
、uuid
和sleep
等不确定性 (Nondeterminstic) 函数【建议】Data Cache,用于存算分离和湖分析场景,建议这两个场景下默认开启
PART/ 05 监控
【必须】通过审计插件把 fe.audit.log 的数据导入一个表方便进行分析慢查询。
通过 Audit Loader 管理 StarRocks 中的审计日志 @ audit_loader[4]
【必须】参考 “https://docs.starrocks.io/zh/docs/2.5/administration/Monitor_and_Alert/ ” 部署 prometheus+grafana 【建议】利用资源隔离大查询熔断,小查询保底
# shortquery_group 资源组用于核心业务重保
CREATE RESOURCE GROUP shortquery_group
TO
(user='rg1_user1', role='rg1_role1', db='db1', query_type in ('select'), source_ip='192.168.x.x/24'),
WITH (
'type' = 'short_query',
'cpu_core_limit' = '10',
'mem_limit' = '20%'
);
# bigquery_group 用于大查询熔断,避免大查询将集群资源打满
CREATE RESOURCE GROUP bigquery_group
TO
(user='rg1_user2', role='rg1_role1', query_type in ('select')),
WITH (
"type" = 'normal',
'cpu_core_limit' = '10',
'mem_limit' = '20%',
'big_query_cpu_second_limit' = '100',
'big_query_scan_rows_limit' = '100000',
'big_query_mem_limit' = '1073741824'
);
大查询定位
查看当前 FE 上正在运行的查询 SQL 命令:
show proc '/current_queries'
QueryId ConnectionId Database:当前查询的 DB User:用户 ScanBytes:当前已扫描的数据量,单位 Bytes ProcessRow:当前已扫描的数据行数 CPUCostSeconds:当前查询已使用的 CPU 时间,单位秒。此为多个线程累加的 CPU 时间,举个例子,如果有两个线程分别占用 1 秒和 2 秒的 CPU 时间,那么累加起来的 CPU 时间为 3 秒 MemoryUsageBytes:当前占用的内存。如果查询涉及到多个 BE 节点,此值即为该查询在所有 BE 节点上占用的内存之和 ExecTime:查询从发起到现在的时长,单位为毫秒
mysql> show proc '/current_queries';
+--------------------------------------+--------------+------------+------+-----------+----------------+----------------+------------------+----------+
| QueryId | ConnectionId | Database | User | ScanBytes | ProcessRows | CPUCostSeconds | MemoryUsageBytes | ExecTime |
+--------------------------------------+--------------+------------+------+-----------+----------------+----------------+------------------+----------+
| 7c56495f-ae8b-11ed-8ebf-00163e00accc | 4 | tpcds_100g | root | 37.88 MB | 1075769 Rows | 11.13 Seconds | 146.70 MB | 3804 |
| 7d543160-ae8b-11ed-8ebf-00163e00accc | 6 | tpcds_100g | root | 13.02 GB | 487873176 Rows | 81.23 Seconds | 6.37 GB | 2090 |
+--------------------------------------+--------------+------------+------+-----------+----------------+----------------+------------------+----------+
2 rows in set (0.01 sec)
show proc '/current_queries/${query_id}/hosts'
Host:BE 节点信息 ScanBytes:已经扫描的数据量,单位 Bytes ScanRows:已经扫描的数据行数 CPUCostSeconds:已使用的 CPU 时间 MemUsageBytes:当前占用的内存
mysql> show proc '/current_queries/7c56495f-ae8b-11ed-8ebf-00163e00accc/hosts';
+--------------------+-----------+-------------+----------------+---------------+
| Host | ScanBytes | ScanRows | CpuCostSeconds | MemUsageBytes |
+--------------------+-----------+-------------+----------------+---------------+
| 172.26.34.185:8060 | 11.61 MB | 356252 Rows | 52.93 Seconds | 51.14 MB |
| 172.26.34.186:8060 | 14.66 MB | 362646 Rows | 52.89 Seconds | 50.44 MB |
| 172.26.34.187:8060 | 11.60 MB | 356871 Rows | 52.91 Seconds | 48.95 MB |
+--------------------+-----------+-------------+----------------+---------------+
3 rows in set (0.00 sec)
关于 StarRocks
StarRocks 全球开源社区也正飞速成长。目前,StarRocks 的 GitHub star 数已达 7600,吸引了超过 330 位贡献者和数十家国内外行业头部企业参与共建,用户社区也有过万人的规模。凭借其卓越的表现,StarRocks 荣获了全球著名科技媒体 InfoWorld 颁发的 2023 BOSSIE Award 最佳开源软件奖项。