StarRocks 最佳实践全指南:表设计(分区 / 排序键 / 分桶)、主键表与查询调优
【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks
本指南源自 StarRocks 官方文档 docs/en/best_practices/overview.md,由资深数据库工程师撰写,聚焦"用效率换成本":合理的表结构与查询调优不仅能显著提升查询速度,更能通过降低存储、CPU 与对象存储(如 S3)API 开销来削减成本。读完本文,你将掌握分区、排序键、分桶三大物理设计旋钮的选型方法论,理解主键表三种索引形态的取舍与内存/存储代价,并学会一套可落地的自顶向下查询调优流程。
StarRocks 的性能并非只来自引擎本身,更来自"表布局与查询模式相匹配"。本文围绕官方 Best Practices 的三大主线展开:通用表设计(分区、表聚类、分桶)、主键表(实时更新场景下的索引与资源权衡)、查询调优(从定位问题到验证迭代的完整闭环),并结合仓库源码中的真实配置项佐证每个结论。
通用表设计:三个物理设计旋钮
官方文档将通用表设计归纳为三份指南:分区、表聚类(排序键)、分桶。三者解决的是不同维度的问题:分区负责粗粒度裁剪与生命周期管理,排序键负责数据物理顺序带来的 I/O 消除,分桶负责并行度与数据分布。
分区(Partitioning):让查询少扫数据
快速分析始于与查询模式匹配的表布局。分区帮助实现四个目标:通过激进的裁剪少扫数据、以元数据级操作管理生命周期(TTL、GDPR 删除、分层存储)、随租户数/数据量/保留窗口平滑扩展、以及控制写放大(新数据落入"热"分区,压缩发生在历史分区)。
分区与分桶:分工不同
| 维度 | 分区(Partitioning) | 分桶(Hash/Random Bucketing) |
|---|---|---|
| 核心目标 | 粗粒度数据裁剪与生命周期控制(TTL、归档) | 分区内部的细粒度并行与数据局部性 |
| 规划器可见性 | 分区是 Catalog 对象,FE 可基于谓词跳过 | 仅等值谓词支持桶裁剪 |
| 生命周期操作 | DROP PARTITION仅元数据操作,适合 GDPR 删除、月度滚动 | 桶不可单独删除,只能通过ALTER TABLE ... MODIFY DISTRIBUTED BY变更 |
| 典型数量 | 每表 10²–10⁴ 个(按天、周、租户) | 每分区 10–120 个,由BUCKETS xxx控制 |
| 倾斜处理 | 合并/拆分分区,考虑复合/混合方案 | 提高桶数、复合键哈希、隔离"鲸鱼"租户或使用随机分桶 |
| 危险信号 | 超过 10 万个分区会给 FE 带来显著内存占用 | 每 BE 超过 20 万个 Tablet、单 Tablet 超 10 GB 可能引发压缩问题 |
何时需要分区
| 表类型 | 是否分区 | 典型键 |
|---|---|---|
| 事实表 / 事件流 | 是 | date_trunc('day', event_time) |
| 超大维表(数十亿行) | 视情况 | 时间或业务键变更日期 |
| 小型维表 / 查找表 | 否 | 依赖哈希分布 |
如何选择分区键
- 时间优先(默认):若 80% 的查询带时间过滤,优先使用
date_trunc('day', dt)。 - 租户隔离:需要按租户管理数据时,将
tenant_id加入分区键。 - 保留对齐:把计划清理的列放入分区键。
- 复合键注意分区爆炸:
PARTITION BY tenant_id, date_trunc('day', dt)裁剪完美,但会产生租户数 × 天数个分区,总量需控制在约 10 万以内,否则 FE 内存与 BE 压缩都会受影响。
粒度选择
PARTITION BY date_trunc('day', dt)的粒度可按场景调整为 "hour"、"day"、"month" 等,函数详情见 date_trunc。
| 粒度 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 日(默认) | 大多数 BI 与报表 | 分区少(每年 365 个)、TTL 简单 | 对"最近 3 小时"类查询精度不足 |
| 小时 | 每天写入量超 2× Tablet 数、IoT 突发 | 热点隔离;每天 24 个分区 | 每年约 8700 个分区 |
| 周 / 月 | 历史归档 | 元数据极小、易于合并 | 裁剪较粗 |
经验法则:每个分区控制在 ≤100 GB,跨副本的每分区 Tablet 数 ≤2 万。自 3.4 版本起,StarRocks 还支持通过将历史分区合并为更粗粒度,实现混合粒度分区。
实战模板
单租户点击流事实表:
CREATE TABLE click_stream ( user_id BIGINT, event_time DATETIME, url STRING, ... ) DUPLICATE KEY(user_id, event_time) PARTITION BY date_trunc('day', event_time) DISTRIBUTED BY HASH(user_id) BUCKETS xxx;多租户 SaaS 指标(推荐模式 A,按时间裁剪、同租户数据共置):
CREATE TABLE metrics ( tenant_id INT, dt DATETIME, metric_name STRING, v DOUBLE ) PRIMARY KEY(tenant_id, dt, metric_name) PARTITION BY date_trunc('DAY', dt) DISTRIBUTED BY HASH(tenant_id) BUCKETS xxx;"鲸鱼"租户复合模式(模式 B,需警惕分区爆炸):
CREATE TABLE activity ( tenant_id INT, dt DATETIME, id BIGINT, .... ) DUPLICATE KEY(dt, id) PARTITION BY tenant_id, date_trunc('MONTH', dt) DISTRIBUTED BY HASH(id) BUCKETS xxx;表聚类(Table Clustering):排序键是杠杆最高的物理设计旋钮
一个深思熟虑的排序键(Sort Key)是 StarRocks 中杠杆率最高的物理设计参数。以遥测系统为例:每天数十亿行、每行带device_id与ts,定义ORDER BY (device_id, ts)可带来——按device_id的点查毫秒级返回、按设备过滤近期时间窗口的仪表盘查询大量裁剪、GROUP BY device_id的流式聚合、以及按设备相近时间戳的连续序列带来的更高压缩比。
CREATE TABLE telemetry ( device_id VARCHAR, ts DATETIME, value DOUBLE ) ENGINE=OLAP PRIMARY KEY(device_id, ts) PARTITION BY date_trunc('day', ts) DISTRIBUTED BY HASH(device_id) BUCKETS 16 ORDER BY (device_id, ts);排序键的四大收益
1. 海量 I/O 消除——Segment 与 Page 裁剪。每个 Segment 和 64 KB Page 都存储所有列的 min/max 值,谓词落在范围之外时 StarRocks 直接跳过整块数据、绝不触碰磁盘。例如ORDER BY (tenant_id, ts)下,查询WHERE tenant_id = 42 AND ts BETWEEN '2025-05-01' AND '2025-05-07'时,只有首键为 42 的 Segment 被纳入考虑,其中也仅 ts 窗口与这七天重叠的 Page 被扫描——1000 亿行的表可能只需扫描不到 10 亿行,把分钟级查询变成秒级。
2. 毫秒级点查——稀疏前缀索引。前缀索引每约 1000 行存储一个排序键值,二分查找定位到正确 Page 后一次磁盘读(通常已命中缓存)即返回行。对ORDER BY (order_id)的表执行WHERE order_id = 982347234,在 500 亿行规模下也只需约 50 次键比较,冷缓存下延迟低于 10 ms。
3. 更快的排序聚合。当排序键与GROUP BY对齐时,StarRocks 在扫描过程中直接做流式聚合,无需排序或哈希表。对ORDER BY (device_id, ts)的表执行GROUP BY device_id,引擎边流入边分组,充分利用 CPU 缓存局部性、跳过中间物化。对高基数列,流式聚合的吞吐通常比哈希聚合提升 2–3 倍。
4. 更高压缩比与更热缓存。有序数据呈现小增量或长游程,加速字典、RLE、frame-of-reference 编码;紧凑的 Page 顺序流过 CPU 缓存。文档给出的实测案例:按(device_id, ts)排序的遥测表,比未排序摄入的同一数据 LZ4 压缩比提升 1.8 倍、CPU/扫描开销降低 25%。
排序键如何工作:从写入到读取的生命周期
写路径:行落入 MemTable 后按声明的排序键排序,刷新为新 Rowset(含一个或多个有序 Segment);后台 cumulative/base 压缩任务把小 Rowset 合并为大 Rowset、回收删除并降低 Segment 数,因所有源 Rowset 共享同一顺序而无需重新排序;每个 Tablet 同步复制到对端 BE,保证副本间顺序一致。
存储层级:分区(粗粒度逻辑切片,启用规划期裁剪并隔离 TTL/批量加载生命周期操作)→ Tablet(分区内按哈希/随机分桶、跨 BE 独立复制,行按排序键物理有序)→ MemTable(约 96 MB 内存写缓冲,落盘前按声明键排序,保证每个磁盘 Segment 天然有序)→ Rowset(一次 flush、流式加载或压缩周期产生的不可变 Segment 束,追加式设计支持并发摄入与无锁读)→ Segment(约 512 MB 自包含列式文件,携带数据页与裁剪索引)。
Segment 文件内部(自顶向下):64 KB 列数据页(Dictionary/RLE/Delta 编码,默认 LZ4 压缩)→ Ordinal 索引(行序 → 页偏移)→ Zone-map 索引(每页与整个 Segment 的 min/max/has_null,裁剪第一道防线)→ Short-key(前缀)索引(每约 1000 行一个、基于排序键前 36 字节的稀疏二分表,支持毫秒级点查/范围查找)→ Footer 与魔数(各索引偏移与完整性校验,StarRocks 仅内存映射尾部即可发现其余结构)。
读路径:规划期分区裁剪(dt BETWEEN谓词只打开匹配分区目录)→ 规划期 Tablet 裁剪(等值过滤含哈希分布列时计算目标 Tablet ID)→ 前缀索引查找(前导排序列定位精确 Segment/Page)→ Zone-map 裁剪(按 min/max 丢弃不命中谓词窗口的块)→ 向量化扫描与延迟物化(存活的列页顺序流过 CPU 缓存,仅物化被引用的行与列)。由于每次 flush 都以键序提交数据,各裁剪层层层叠加,最终实现数十亿行表上的亚秒级扫描。
如何选择有效的排序键
- 从工作负载洞察出发:分析 Top-N 查询模式——等值谓词(
=/IN)列是理想的前导候选;范围谓词(时间戳、数值范围)通常跟在等值列之后;若范围列也出现在GROUP BY中,将其前移可启用排序聚合;高频 join/分组键也应考虑前置。同时度量基数:高基数列(数百万去重值)裁剪效果最好。 - 启发式规则:顺序为(高选择性等值列)→(主范围列)→(聚类辅助列);低基数列放在高基数列之前可增强压缩;宽度保持在 3–5 列,过宽会拖慢摄入并撑爆 36 字节前缀索引限制;过长的前导字符串列可能占满 36 字节前缀索引额度,导致后续排序列无法被有效索引,削弱前缀索引裁剪力、降低点查性能。
- 与其他设计旋钮协同:分区键应比前导排序列更粗(例如
PARTITION BY date+ORDER BY (tenant_id, ts)),让分区裁剪先移除整段日期范围、排序裁剪再处理内部;分桶与排序使用相同列目的不同(分桶保证集群内均匀分布,排序实现 I/O 消除);主键表默认以主键为排序键,但也可附加列优化物理顺序;聚合表与明细表遵循上述分析谓词驱动的排序键策略。
参考模板:
| 场景 | 分区 | 排序键 | 理由 |
|---|---|---|---|
| B2C 订单 | date_trunc('day', order_ts) | (user_id, order_ts) | 多数查询先按用户、再按近期时间范围过滤 |
| IoT 遥测 | date_trunc('day', ts) | (device_id, ts) | 设备维度时序读取占主导 |
| SaaS 多租户 | tenant_id | (dt, event_id) | 分区实现租户隔离;按天聚簇服务仪表盘 |
| 维度查找 | 无 | (dim_id) | 小表纯点查,单列足矣 |
分桶(Bucketing):哈希分桶还是随机分桶
分桶是一份选择哈希分桶与随机分桶的速查指南。先看快速对比:
| 维度 | 哈希分桶 | 随机分桶 |
|---|---|---|
| 示例 | DISTRIBUTED BY HASH(id) BUCKETS 16 | DISTRIBUTED BY RANDOM |
| 键声明 | 必须HASH(col1, …) | 无——行按 round-robin 分配 |
| 省略时的初始桶数 | 创建时自动选择,之后固定 | 创建时自动选择;设置 bucket_size 后可增长 |
| Tablet 拆分/收缩 | 手动ALTER … BUCKETS | 自动拆分(仅增长,v3.2+) |
| 倾斜抵抗力 | 取决于键基数 | 高——设计上均匀 |
| 桶裁剪 | ✅(过滤、join) | 🚫(全 Tablet 扫描) |
| Colocate join | ✅ | 🚫 |
| 本地聚合 / bucket-shuffle join | ✅ | 🚫 |
| 支持的表类型 | 全部 | 仅明细(Duplicate Key)表 |
哈希分桶
行按一个或多个列的哈希值分配到 Tablet,创建后 Tablet 数固定(除非手动 ALTER)。要求:必须预先选择稳定、均匀、高基数的键——键基数通常应为 BE 节点数的 1000 倍以上,以防范桶间数据倾斜;初始桶大小应合理,理想为 1–10 GB。
优势:查询局部性(选择性过滤与 join 触碰更少 Tablet);colocate join(事实表/维表共享哈希键实现高速 join);可预测布局(同键行始终落在一起);本地聚合与 bucket-shuffle join(跨分区哈希布局一致,减少大 join 的 shuffle 开销)。劣势:数据分布倾斜时易出现热 Tablet;Tablet 数静态,扩容需维护 DDL;Tablet 不足会损害摄入、压缩与查询并行度;Tablet 过多会膨胀元数据。
实战示例:维表-事实表 join 与 Tablet 裁剪
-- 事实表按 (customer_id) 哈希分桶 CREATE TABLE sales ( sale_id bigint, customer_id int, sale_date date, amount decimal(10,2) ) ENGINE = OLAP DISTRIBUTED BY HASH(customer_id) BUCKETS 48 PARTITION BY date_trunc('DAY', sale_date) PROPERTIES ("colocate_with" = "group1"); -- 维表以相同键与桶数与 sales 表 colocate CREATE TABLE customers ( customer_id int, region varchar(32), status tinyint ) ENGINE = OLAP DISTRIBUTED BY HASH(customer_id) BUCKETS 48 PROPERTIES ("colocate_with" = "group1"); -- Tablet 裁剪:仅访问单个 Tablet SELECT sum(amount) FROM sales WHERE customer_id = 123; -- 本地聚合:跳过 shuffle 聚合阶段 SELECT customer_id, sum(amount) AS total_amount FROM sales GROUP BY customer_id ORDER BY total_amount DESC LIMIT 100; -- Colocate join:各 BE 本地配对 join,无需网络 shuffle SELECT c.region, sum(s.amount) FROM sales s JOIN customers c USING (customer_id) WHERE s.sale_date BETWEEN '2025-01-01' AND '2025-01-31' GROUP BY c.region;何时使用哈希分桶:模式稳定、分布过滤/join 键明确;受益于桶裁剪的数仓负载;需要 colocate join / bucket shuffle join / 本地聚合等特定优化;使用聚合表或主键表。
随机分桶
行按 round-robin 分配、无需指定键;设置PROPERTIES ("bucket_size"="<bytes>")后(v3.2+),StarRocks 会随分区增长自动拆分 Tablet。优势:零设计债(无键、无桶计算);写入抗倾斜(磁盘与 BE 间压力均匀);弹性增长(Tablet 拆分保持摄入速度)。劣势:无桶裁剪(每个查询扫描分区内全部 Tablet);无 colocate join(无键布局阻碍局部性);目前仅支持明细表。
何时使用:日志/事件表或键会变化/倾斜的多租户 SaaS 表;写入密集、均匀摄入吞吐至关重要的管道。
运维指南:随机分桶建议设置桶大小(如 1 GiB)以启用自动拆分;哈希分桶需监控 Tablet 大小,在 Tablet 超过 5–10 GiB 前重新分片。
主键表(Primary Key Tables):实时更新的代价与取舍
主键表使用 StarRocks 自研的新存储引擎,核心优势是支持实时数据更新,同时保持复杂 ad-hoc 查询的高效性能。实时业务分析中,决策者可以基于最新数据实时分析,缓解数据分析中的延迟问题。但主键并非"免费的午餐"——使用不当会造成不必要的资源浪费。
选择主键索引类型
主键索引(Primary Index)是主键表最关键的组件,存储主键值与数据行位置之间的映射。目前支持三种类型:
- 全内存主键索引(不推荐,会造成显著内存浪费):
PROPERTIES ( "enable_persistent_index" = "false" );- 本地磁盘持久化主键索引:
PROPERTIES ( "enable_persistent_index" = "true", "persistent_index_type" = "LOCAL" );- 云原生持久化主键索引(shared-data 弹性集群推荐):
PROPERTIES ( "enable_persistent_index" = "true", "persistent_index_type" = "CLOUD_NATIVE" );官方明确不建议使用内存索引。若使用 shared-data(弹性)集群,推荐云原生持久化主键索引:与本地磁盘持久化索引不同,它将完整索引数据存放于远端对象存储,本地磁盘仅作缓存。相比本地磁盘持久化索引,其优势是:不依赖本地磁盘容量;数据分片再平衡后无需重建索引。
选择主键
主键通常不帮助加速查询——可通过ORDER BY指定不同于主键的列作为排序键来加速查询。因此选主键时只需考虑导入与更新过程中的唯一性。
主键越大,消耗的内存、I/O 等资源越多,因此一般建议避免选择过多或过大的列作为主键。主键默认最大 128 字节,由be.conf中的primary_key_limit_size控制(对应源码 be/src/common/config.h 中CONF_mInt32(primary_key_limit_size, "128")的默认值)。可增大该值以支持更大的主键,但需注意资源消耗上升。
持久化索引空间占用估算:
- 存储空间:
(key size + 8 bytes) * row count * 50%(50% 为估计压缩效率,实际取决于数据本身); - 内存空间:
min(l0_max_mem_usage * tablet cnt, update_memory_limit_percent * BE process memory)。
内存使用监控与调优
主键表内存可通过 mem_tracker 监控:
// 查看整体内存统计 http://be_ip:be_http_port/mem_tracker // 查看主键表内存统计 http://be_ip:be_http_port/mem_tracker?type=update // 查看更详细的主键表内存统计 http://be_ip:be_http_port/mem_tracker?type=update&upper_level=4mem_tracker中的update项记录主键表使用的全部内存(主键索引、delete vector 等),也可通过指标监控服务(如 Grafana)观察。
若对内存敏感、希望降低主键表导入过程的内存消耗,可调整以下配置(均在be.conf):
l0_max_mem_usage = (小于 104857600 的值,默认 104857600) // Shared-nothing 集群 transaction_apply_worker_count = (小于 CPU 核数的值,默认为 CPU 核数) // Shared-data 集群 transaction_publish_version_worker_count = (小于 CPU 核数的值,默认为 CPU 核数)l0_max_mem_usage控制每个 Tablet 持久化主键索引的最大内存使用(源码 config.h 默认 104857600 字节);transaction_apply_worker_count与transaction_publish_version_worker_count均控制处理主键表 upsert/delete 的最大线程数。但需谨记:降低l0_max_mem_usage可能增加 I/O 压力,减少 worker 数可能拖慢数据摄入。
压缩资源、数据新鲜度与查询延迟的三角权衡
相比其他模型,主键表在导入、更新、删除时需要额外的主键索引查找与 delete vector 生成操作,引入额外资源开销,因此需在以下三者间权衡:压缩资源限制、数据新鲜度、查询延迟。
数据新鲜度 & 查询延迟:若既要更好新鲜度又要更好查询延迟,意味着高频写入且需尽快压缩,需要更多压缩资源:
// shared-data be.conf compact_threads = 4 // shared-nothing be.conf update_compaction_num_threads_per_disk = 1 update_compaction_per_tablet_min_interval_seconds = 120可增大compact_threads、update_compaction_num_threads_per_disk或减小update_compaction_per_tablet_min_interval_seconds(源码默认值见 config.h、config.h)来投入更多压缩资源。如何判断压缩跟不上高频写入?
- Shared-data 集群:压缩跟不上会导致摄入变慢甚至写入失败。摄入变慢可通过
show proc '/transactions/{db_name}/running'检查,若 ErrMsg 出现Partition's compaction score is larger than 100.0, delay commit for xxxms. You can try to increase compaction concurrency,说明发生摄入变慢;若出现Failed to load data into partition xxx, because of too large compaction score...,说明摄入已停止。 - Shared-nothing 集群:无摄入变慢策略,压缩跟不上会直接报错:
Failed to load data into tablet xxx, because of too many versions...。
数据新鲜度 & 压缩资源受限:压缩资源有限但需维持足够新鲜度,就要牺牲部分查询延迟:
- Shared-data 集群(
fe.conf):调大lake_ingest_slowdown_threshold(默认 100,触发摄入变慢的压缩分数阈值)与lake_compaction_score_upper_bound(默认 2000,触发摄入停止的阈值); - Shared-nothing 集群(
be.conf):调大tablet_max_versions(默认 1000,源码见 config.h,触发摄入停止的版本数阈值)。
增大这些配置可容纳更多小数据文件、降低压缩频率,但会影响查询延迟。
查询延迟 & 压缩资源受限:想用有限压缩资源获得好查询延迟,需要降低写入频率、以更大批次摄入数据,具体做法参见各摄入方式章节(降低摄入频率、增大批大小)。
查询调优(Query Tuning):自顶向下的五步闭环
查询调优 是 StarRocks 实现高性能与高可靠性的关键。该目录汇集实用指南、参考资料与可落地的配方,覆盖从编写 SQL 到解读执行细节的每个阶段。有效的查询调优遵循自顶向下流程:
- 识别问题:检测慢查询、高资源占用或异常结果;借助内置监控、查询历史与审计日志快速定位问题查询。参见 Query Tuning Recipes(症状驱动诊断)与 Query Profile Overview(查询历史与 Profile)。
- 收集并分析执行信息:用
EXPLAIN或EXPLAIN ANALYZE获取查询计划;开启并研读 Query Profile 获取详细执行指标。参见 Query Plan Overview、Explain Analyze & Text-Based Profile Analysis 与 Query Profile Overview。 - 定位根因:找出耗时/耗资源最高的 Stage 或 Operator;排查常见问题:join 顺序欠佳、索引缺失、数据分布问题、低效 SQL 模式。参见 Query Profile Metrics 与 Query Tuning Recipes。
- 应用调优策略:SQL 改写(加过滤、避免
SELECT *);Schema 调优(加索引、换表型、调分区/聚类);查询计划调优(必要时用 hint 或变量引导优化器);执行调优(针对负载调会话变量)。参见 Schema Tuning Recipes、Query Hint 与 Query Tuning Recipes。 - 验证并迭代:重跑查询、对比改动前后性能,复查新计划与 Profile,必要时重复。
无论你是 DBA、开发还是数据工程师,这套资源都能帮助你:诊断并解决慢查询或资源密集型查询;理解优化器选择与执行细节;应用最佳实践与高级调优策略。
Query Profile:开启与获取
Query Profile 记录查询涉及的所有工作节点的执行信息,是诊断与调优的利器。开启方式:
SET enable_profile = true; -- 会话级 SET GLOBAL enable_profile = true; -- 全局级生产环境不建议长时间全局开启(有额外开销),可设置big_query_profile_threshold只捕获慢查询(对应 FE 源码 SessionVariable.java 中的BIG_QUERY_PROFILE_THRESHOLD变量):
SET global big_query_profile_threshold = '30s'; -- 仅超 30 秒的查询 SET global big_query_profile_threshold = '500ms'; -- 仅超 500 毫秒 SET global big_query_profile_threshold = '60m'; -- 仅超 60 分钟长查询可用 Runtime Query Profile(v3.1+)在执行期间按固定间隔上报,默认间隔 10 秒,可用SET runtime_profile_report_interval = 30;调整。关键配置一览:
| 配置项 | 类型 | 合法值 | 默认 | 说明 |
|---|---|---|---|---|
| enable_profile | 会话变量 | true/false | false | 开启 Query Profile |
| pipeline_profile_level | 会话变量 | 1/2 | 1 | 1 合并指标;2 保留原始结构(禁用可视化工具) |
| runtime_profile_report_interval | 会话变量 | 正整数 | 10 | Runtime Query Profile 上报间隔(秒) |
| big_query_profile_threshold | 会话变量 | 字符串 | 0s | 超过该时长的查询开启 Profile(如 '30s'、'500ms'、'60m') |
| enable_statistics_collect_profile | FE 动态 | true/false | false | 为统计信息收集相关查询开启 Profile |
获取方式:Web UI 访问http://<fe_ip>:<fe_http_port>→ 顶部导航点击queries→ 在Finished Queries列表选择目标查询并点击Profile列链接;或通过 SQL 函数get_query_profile(配合last_query_id()获取最近查询 ID、show profilelist;列出近期查询与状态)。自 v3.3.0 起,INSERT INTO FILES()与 Broker Load 也支持 Query Profile。
症状到修复:按 Operator 的调优配方
Query Tuning Recipes 提供"症状 → 根因 → 已验证修复"的实用手册。快速诊断流程:先浏览执行概览(QueryPeakMemoryUsagePerNode > 80%或QuerySpillBytes > 1 GB直接进入内存/溢写配方);再在 Profile UI 按OperatorTotalTime %排序定位最热 Operator;最后匹配各配方的特征指标模式再实施修复。
以 Scan(扫描)Operator 为例,存储引擎依次使用:数据存储(段内压缩编码数据+多种索引)、索引过滤(Bitmap、Bloomfilter、Zonemap、ShortKey、NGram 索引跳过无关数据)、谓词下推(a > 1类简单谓词推到列级求值)、延迟物化(仅从磁盘取所需列与过滤后的行)、非下推谓词求值、投影表达式计算。常见瓶颈与对策包括:冷/慢存储(BytesRead/ScanTime/IOTaskExecTime占主导时换 NVMe/SSD 并启用 Data Cache,按 BEdatacache_*与会话变量enable_scan_datacache配置);过滤下推缺失(PushdownPredicates接近 0 而ExprFilterRows高时,改写为简单比较、避免%LIKE%与宽OR链,或加 zonemap/Bloom 索引/物化视图);线程池饥饿(高IOTaskWaitTime伴低PeakIOTasks时扩容 Data Cache 与热存储);Tablet 数据倾斜(最大与最小OperatorTotalTime差距大时改用高基数列或增桶数);Rowset/Segment 碎片化(RowsetsReadCount/SegmentsReadCount暴涨时手动压缩并批量加载);软删除累积(DeleteFilterRows大时运行 BE 压缩清除)。
聚合(Aggregate)Operator 方面,StarRocks 依据查询模式与优化器决策采用多形态算法:哈希聚合(键可入内存、基数不极端,SIMD 探测的紧凑哈希表)、排序聚合(输入已按 GROUP BY 键有序,零哈希表成本,高偏斜探测下常快 2–3 倍)、可溢写聚合(3.2+,哈希表超出内存限制时混合哈希/合并+磁盘溢写分区,防 OOM 且保持管道并行)。分布式聚合为多阶段:一阶段(DISTRIBUTED BY是GROUP BY子集且分区共置,局部聚合即最终结果)、两阶段 local+global(典型分布式GROUP BY)、三阶段 local+shuffle+final(重度DISTINCT与高基数GROUP BY)、四阶段(重度DISTINCT与低基数GROUP BY时增加阶段避免单点瓶颈)。
总结
StarRocks 的高性能源于"物理设计匹配查询模式"这一原则的层层落实:分区用粗粒度裁剪与元数据级生命周期操作换来 I/O 与运维成本的下降;排序键通过在写路径上建立顺序、在 Segment 内构建微型索引,让读路径上的每层裁剪互相叠加,实现数十亿行表上的亚秒级扫描;分桶在分区内部决定并行度与数据局部性,哈希分桶换取裁剪/colocate/本地聚合能力,随机分桶换取抗倾斜与弹性增长。主键表则以可控的索引内存与压缩资源为代价,换来实时更新与复杂 ad-hoc 查询兼得的能力,其三种索引形态与primary_key_limit_size、l0_max_mem_usage、tablet_max_versions等配置(均可对照 be/src/common/config.h 中的真实默认值)提供了精细的资源调节空间。最后,查询调优用EXPLAIN/Query Profile 等工具支撑的五步闭环,将上述设计决策与运行时行为连接起来,形成"设计-观测-调优-验证"的完整方法论。
继续深入可参阅:分区、表聚类、分桶、主键表、查询调优概览、Query Profile 概览 与 Query Tuning Recipes。
【免费下载链接】starrocksThe world's fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考