一、引言
StarRocks 的查询链路可以拆成规划、调度、执行三段:FE 解析、分析并优化 SQL,调度器和 Coordinator 将计划分发到参与执行的后端节点,最终由 Pipeline Engine 执行计划。

这条链路的关键不是"并行"两个字,而是每一层都减少下一层的负担。分区裁剪和索引减少 Scan,CBO 减少错误 Join 顺序和错误数据交换,Pipeline 减少线程阻塞和调度空洞,向量化减少解释器和分支开销,物化改写绕过重复计算。
二、向量化执行
向量化执行的核心思想是把"逐行调用算子"变成"按列批量处理数据块"。在 OLAP 场景下,过滤、投影、表达式计算、聚合和 Join Probe 往往对大量同类型数据重复执行;以列为单位批量处理,能够更好利用 CPU Cache、SIMD 指令和紧凑内存布局,也减少虚函数调用、解释分支和对象装箱带来的开销。

在工程直觉上,向量化让 CPU 从"每处理一行都重新进入一次算子"变成"在紧凑数组上连续跑一段循环"。这也是为什么低选择率过滤、简单表达式、大批量聚合、宽表扫描中的部分列读取,往往能体现列式加向量化的优势。
1.什么时候变慢
向量化并不自动消除所有瓶颈。当 SQL 使用复杂 UDF、低效正则、无法下推的表达式、大量高基数字符串处理,或者 Join、Sort、Window 等算子受内存和 Shuffle 约束时,CPU 批处理优势可能被表达式成本、内存访问随机性和数据搬运成本抵消。此时 Profile 中常见信号是某些表达式算子 CPU 时间突出、Scan 输出行数远高于预期、Join Build 或 Probe 阶段耗时集中。
三、CBO 与统计信息
CBO 决定"怎么跑",SQL 到达 StarRocks 后先被解析为逻辑执行计划,CBO 会把逻辑计划重写和转换成多个物理执行计划,并根据 CPU、内存、网络和 I/O 等成本估算选择总代价最低的物理计划。
markdown
逻辑 SQL
|
v
等价变换空间:
- Join reorder
- Predicate pushdown
- Aggregate pushdown
- Join strategy: Broadcast / Shuffle / Colocate / Bucket Shuffle
- MV rewrite candidates
|
v
成本估算 = rows + NDV + null_count + min/max + histogram + network/memory/I/O
|
v
选择总代价最低计划
统计信息是 CBO 的"视力",StarRocks 自动或手动收集 row_count、data_size、NDV、null_count、min、max 等基础统计;2.4 引入直方图以更准确表达倾斜分布;3.5.0 支持多列联合统计,用于缓解多列相关性下的基数估算误差。
| 现象 | CBO 可能误判 | 常见后果 | 优先检查 |
|---|---|---|---|
| 统计信息陈旧 | 行数、NDV、选择率估错 | Join 顺序错误,大表提前参与 Join | EXPLAIN COSTS、统计信息健康度 |
| 数据倾斜 | 平均分布假设失真 | 某些 Driver 或 Tablet 执行时间明显长 | Profile 中 rows/time 分布、直方图 |
| 多列强相关 | 独立性假设导致基数估错 | 聚合或 Join 输出行数估计偏离 | 多列联合 NDV、谓词列统计 |
| 过滤列缺少统计 | 谓词选择率不准 | Broadcast/Shuffle 选择不合适 | Predicate Column 统计 |
1.什么时候变慢
如果 CBO 估错,后面的向量化和 Pipeline 只是在"更快地执行一个差计划"。典型慢路径包括:小表被估成大表导致无法 Broadcast,大表被估成小表导致 Broadcast 爆内存,Join 顺序让中间结果膨胀,聚合下推没有发生,或者物化视图候选没有被选中。
建议从底向上阅读计划,关注 Scan、Join、Aggregation、Sort、Exchange、Predicate Pushdown 等节点,并用EXPLAIN ANALYZE和 Query Profile 对照真实执行。
四、Pipeline Engine
Pipeline Engine 解决的是执行阶段"如何持续占用 CPU"。查询的执行阶段使用 Pipeline Execution Engine,一个 FragmentInstance 内部由 Pipeline 串联算子,多个 PipelineDriver 可以在不同 CPU Core 上并行运行同一 Pipeline。
lua
Fragment
|
+-- FragmentInstance on BE-1
| |
| +-- Pipeline 0: Scan -> Filter -> LocalAgg
| | +-- Driver 0 on Core A
| | +-- Driver 1 on Core B
| |
| +-- Pipeline 1: Exchange -> GlobalAgg -> Sink
|
+-- FragmentInstance on BE-2
|
+-- 同构 Pipeline / Driver 并行执行
传统 Volcano 模型容易在算子之间形成阻塞点,尤其是 Join Build、Exchange、Sort、Global Aggregate 等阶段。Pipeline 化把可串联的算子组成执行管道,并通过 Driver 运行在 Core 上,让上游产生数据、下游消费数据的过程更细粒度、更易调度,也更适合高并发下的资源共享。
1.什么时候变慢
Pipeline 需要足够的可并行工作和合适的资源。慢路径包括:扫描 Tablet 数太少导致并行度不足,单个 Tablet 或分区过大导致局部热点,某个 Driver 因数据倾斜拖尾,Exchange 网络传输成为瓶颈,或资源组和并发查询让 CPU 时间被切碎。官方 Profile 文档提到 Query Profile 会记录参与查询的工作节点执行信息,并可通过慢查询阈值只采集超过指定耗时的查询,避免生产环境长期全局开启 Profile 带来额外开销。
五、列存压缩与索引
StarRocks 底层采用列式存储,列数据组织在数据页中,其内置 Prefix、Ordinal、ZoneMap 索引,并支持手动创建 Bitmap、Bloom Filter、N-Gram Bloom Filter、倒排索引和向量索引等。对 OLAP 来说,列存的直接收益是"只读需要的列",压缩的收益是降低 I/O 和内存带宽压力,索引的收益是过滤掉本不该进入算子的块。
rust
一张宽表:100 列,查询只取 6 列
行存扫描: [c1 c2 c3 ... c100] x N rows -> 读很多无关字段
列存扫描: c3 + c7 + c9 + c20 + c21 + c30 -> 只读相关列
ZoneMap / Prefix / Bloom 等过滤:
Segment/Page 统计信息 -> 判断"不可能命中" -> 跳过读取
ZoneMap 保存数据块的 min、max、是否含 NULL 等统计信息,查询时可快速判断数据块是否可被过滤;Prefix Index 在写入时按排序键排序,每 1024 行形成一个逻辑块并记录首行排序键值,过滤条件匹配前缀时可减少扫描数据量;合适的分区和分桶可以实现均匀分布、减少扫描量并充分利用集群并行处理能力。
实时更新场景下,相比 Merge-On-Read 的 Unique Key 表,Primary Key 表可带来 3 到 10 倍查询性能提升。Unique Key 表和 Aggregate 表采用 Merge-On-Read,读取时需要在线合并多版本数据,并且 Merge 算子会影响谓词和索引下推;Primary Key 表采用 Delete+Insert 策略、主键索引和 DelVector,查询时只需读取同一主键的最新记录,避免在线合并,并允许过滤算子和索引更好地下推。
1.什么时候变慢
列存怕"读得太多"。如果分区粒度过粗、排序键和高频过滤条件错位、Bucket 数量或 Bucket Key 不匹配数据规模与 Join 模式、查询总是读取大量宽列、低选择率谓词不能利用索引,列存和压缩只能降低部分 I/O,无法阻止下游算子处理海量数据。数据分布文档也强调,分区列和粒度需要结合数据量、查询模式和数据管理粒度选择;当业务查询模式变化时,原有分布策略可能不再适合。
六、物化视图改写
物化视图改写是"少重复算"的关键。异步物化视图是保存一个或多个基表预计算结果的特殊物理表;当查询基表时,StarRocks 可判断预计算结果是否可复用,并直接从物化视图读取,从而避免重复的 Join、聚合或复杂 ETL 计算。

StarRocks 对异步物化视图使用基于 SPJG(Select-Project-Join-Group-by)形式的透明查询改写算法;用户不需要修改查询语句,系统可以把对基表的查询自动改写为对包含预计算结果的物化视图查询。改写能力包括强一致性、可容忍一定陈旧度的 Staleness Rewrite、多表 Join、聚合改写、嵌套物化视图、Union Rewrite、基于视图的物化视图、外部 Catalog 物化视图和复杂表达式改写。
| 场景 | 物化视图为什么快 | 变慢或不命中的原因 |
|---|---|---|
| 高频指标预聚合 | 把 Group By 和聚合结果提前算好 | 维度粒度不匹配、聚合函数或表达式不满足改写条件 |
| 多表宽 Join | 把事实表和维表 Join 结果提前扁平化 | 基数保持约束、唯一约束、外键约束或 Join 类型信息不足 |
| 湖仓查询加速 | 把远端对象存储扫描转成本地或管理内的预计算读取 | 外部 Catalog 限制、刷新滞后、分区映射不合适 |
| 冷热数据分离 | 热数据走 MV,历史数据通过 Union Rewrite 回表 | TTL、分区刷新策略和查询时间范围不匹配 |
1.什么时候变慢
物化视图不是免费午餐。创建 MV 会占用存储,刷新会消耗计算资源;如果全量刷新、分区不对齐或上游更新频繁,刷新成本可能抵消查询收益。
为避免全量刷新耗尽资源并导致任务失败,建议基于分区基表创建分区物化视图,使基表分区更新时只刷新对应 MV 分区。
七、慢查询诊断指南
把 StarRocks 的快路径反过来看,就能得到一套性能判断框架。慢查询通常不是"引擎慢",而是 Scan 放大、Shuffle 放大、Join 中间结果放大、聚合状态放大、刷新成本放大、或资源争用放大。
scss
性能判断漏斗
1. 是否少读?
partitionRatio / tabletRatio / predicate / pushed-down filters
|
2. 是否少搬?
Exchange(BROADCAST/SHUFFLE/GATHER) 行数与字节数
|
3. 是否少算?
MV rewrite / pre-aggregation / local aggregate / runtime filter
|
4. 是否并行均衡?
PipelineDriver 时间分布 / Tablet 倾斜 / BE 负载
|
5. 是否资源足够?
Memory / Spill / CPU queue / Network / Resource Group
另外,诊断 StarRocks 查询性能,建议先判定计划是否合理,再判定执行是否符合计划。
sql
建议诊断顺序
SQL 文本
|
+--> EXPLAIN / EXPLAIN VERBOSE:计划结构是否合理
|
+--> EXPLAIN COSTS:估算行数和成本是否离谱
|
+--> EXPLAIN ANALYZE:估算与实际差异
|
+--> Query Profile:Scan / Join / Exchange / Agg / Sort / Spill / Driver 倾斜
|
+--> 表设计复核:分区、分桶、排序键、索引、统计信息、MV
以下是常见的慢查询信号与优化:
| 慢查询信号 | 可能根因 | 验证方式 | 修正方向 |
|---|---|---|---|
| partitionRatio、tabletRatio 接近全扫 | 分区裁剪失败或谓词不匹配分区列 | EXPLAIN VERBOSE 查看 Scan 节点 | 调整分区表达式、SQL 谓词写法和分区粒度 |
| Scan 输出行数远高于预期 | 谓词未下推、排序键不匹配、索引无效 | Profile 中 Scan Rows、PushdownPredicates | 重设排序键,补充 Bloom/Bitmap/倒排索引 |
| Join 后行数膨胀 | Join 顺序或基数估算错误 | EXPLAIN COSTS 与实际 Profile 对比 | 更新统计信息,补直方图或多列统计 |
| Exchange 时间高 | Shuffle 数据量大或 Join 策略不合适 | 查看 Exchange 行数、字节数、网络耗时 | Colocate Join、Bucket Shuffle、Broadcast 小表 |
| 少数 Driver 拖尾 | 数据倾斜或 Tablet 分布不均 | Profile Driver 级耗时和 rows 分布 | 重分桶、调整 Bucket Key、拆热点分区 |
| Spill 或内存峰值高 | Join Build、Sort、Agg 状态过大 | Profile Memory、Spill 指标 | 降低中间结果、预聚合、物化视图、资源组隔离 |
| MV 未命中 | 查询和 MV 不满足改写约束 | EXPLAIN 是否出现 MV Scan | 对齐 SPJG、分区、维度、聚合函数和约束信息 |