首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >StarRocks 查询探秘(四):Optimizer优化器之规则重写

StarRocks 查询探秘(四):Optimizer优化器之规则重写

原创
作者头像
用户11806606
发布2025-09-11 20:43:53
发布2025-09-11 20:43:53
2730
举报

Optimizer 优化器是查询引擎的“大脑”,通过规则重写(RBO)和基于成本的优化(CBO),从众多执行计划中挑选最优方案,交给执行引擎。本文基于 StarRocks-3.4.0 源码,结合案例深入剖析优化器的规则重写机制,揭示如何通过减少无效数据计算提升查询性能。


Optimizer的核心功能

Optimizer优化SQL查询执行计划,主要通过以下两种方式:

  • 规则驱动优化(optimizeByRule):基于一系列预定义的规则重写逻辑计划。
  • 成本驱动优化(optimizeByCost):基于成本模型选择最低代价成本的物理执行计划。

在《极致性能背后的黑科技?这个世上没有“银弹”!(二)》一文中,从数据量这一角度分析StarRocks是如何让查询速度更快,简而言之,就是"越少无效的数据参与计算,越快的速度"。而本文要介绍的部分规则重写,其实也是这一思想的体现,目的就是让更个环节减少无效的数据参与计算。


Optimizer规则重写的实现

在Optimizer.java中,logicalRuleRewrite 是 StarRocks 优化器实现规则重写的核心方法,通过一系列逻辑优化规则对 Logical Plan 进行重写和优化,传进去的 tree 正是上一阶段构建好的 Logical Plan。

图片
图片

RuleSetType 枚举定义了一系列优化规则集,用于在 logicalRuleRewrite 方法中对查询计划进行重写和优化,旨在减少查询的计算开销、IO 开销和数据处理量,从而提高查询性能。

代码语言:javascript
复制
public enum RuleSetType {        LOGICAL_TRANSFORMATION,    // 逻辑计划转换        PHYSICAL_IMPLEMENTATION,   // 物理计划实现        MERGE_LIMIT,               // 合并 LIMIT 操作        PRUNE_COLUMNS,             // 列裁剪        PUSH_DOWN_PREDICATE,       // 谓词下推        SUBQUERY_REWRITE_COMMON,   // 子查询重写        ...        VECTOR_REWRITE             // 向量化重写}

案例分析

以下面2张表作为例子,建表语句及字段如下:

代码语言:javascript
复制
CREATE TABLE IF NOT EXISTS customers (        id BIGINT NOT NULL,        name VARCHAR(100),        age INT,        email VARCHAR(100)) ENGINE = olapDUPLICATE KEY (id)DISTRIBUTED BY HASH(id)PROPERTIES (        "replication_num" = "3");     CREATE TABLE IF NOT EXISTS orders (        order_id BIGINT,        customer_id INT,        order_date DATE,        amount DECIMAL(10,2)) ENGINE = olap    DUPLICATE KEY(order_id)DISTRIBUTED BY HASH(order_id)PROPERTIES (        "replication_num" = "3");

常见的重写规则

一. 谓词下推(PUSH_DOWN_PREDICATE)

谓词下推的核心思想是尽早过滤数据,避免将不必要的数据加载到计算层。通过将过滤条件下推到存储层(如文件扫描),可以在数据读取时直接跳过不符合条件的数据,减少 I/O 和计算开销。

代码语言:javascript
复制
SELECT o.order_id, o.amount, c.nameFROM orders oJOIN customers c ON o.customer_id = c.customer_idWHERE o.order_date = '2025-08-01' AND c.country = 'China';

优化前的逻辑计划树

过滤条件 o.order_date = '2025-08-01' 和 c.country = 'China' 在 Join 后应用,导致 orders 和 customers 表扫描了全表数据。

图片
图片

优化后最终的执行计划

通过Explain命令查看,将 o.order_date = '2025-08-01' 下推到 orders 表的扫描节点,c.country = 'China' 下推到 customers 表的扫描节点。扫描时只读取满足条件的行,减少了 Join 操作的输入数据量。

图片
图片

二. 列裁剪 (PRUNE_COLUMNS)

列裁剪移除查询计划中不需要的列,减少数据传输和计算开销。

代码语言:javascript
复制
SELECT order_id, customer_idFROM ordersWHERE order_date = '2025-08-01';

优化前的逻辑计划树

扫描 orders 表,读取所有列:order_id, customer_id, order_date, amount

图片
图片

优化后最终的执行计划

通过Explain命令查看,只扫描查询所需的列,最终只输出列:order_id, customer_id。减少了读取的数据量,对宽表(列数多)或数据量大的场景,列裁剪能显著减少 IO 和计算成本。这也是在SQL中尽量避免使用 "SELECT * "的原因。

图片
图片

三. LIMIT下推合并 (MERGE_LIMIT)

Limit 下推是指将查询中的 LIMIT 子句尽可能“下推”到 SCAN 阶段,从而减少后续操作需要处理的数据量;Limit Merge 是一种优化技术,用于合并多个 LIMIT 子句,避免重复计算。当一个查询中包含多个 LIMIT 子句时,StarRocks 会尝试将它们合并为一个更高效的 LIMIT。

代码语言:javascript
复制
SELECT order_id, customer_idFROM ordersWHERE order_date = '2025-08-01' LIMIT 1;

优化前的逻辑计划树

在没有 LIMIT 下推的情况下,查询会先扫描所有符合 order_date = '2025-08-01' 条件的数据,然后在最后阶段应用 LIMIT 1。

图片
图片

SCAN 节点没有 LIMIT 限制

图片
图片

优化后最终的执行计划

通过 LIMIT 下推,SCAN 在扫描数据时直接限制 LIMIT 1,当读取满足 order_date = '2025-08-01' 条件的一条数据后,就停止读取,从而减少 IO 和计算开销。

图片
图片

除上述规则外,StarRocks 还支持以下优化:

  • 分区裁剪(PARTITION_PRUNE):跳过无关分区。
  • 子查询重写(SUBQUERY_REWRITE), 优化子查询逻辑。
  • 物化视图重写(MV_REWRITE), 利用物化视图加速查询。

.......

在下一篇文章《StarRocks 查询探秘(五):Optimizer优化器之基于成本优化》中,将以一个线上外表查询计划构建频繁超时为例,深入分析 CBO的原理,敬请期待!

Optimizer 优化器是查询引擎的“大脑”,通过规则重写(RBO)和基于成本的优化(CBO),从众多执行计划中挑选最优方案,交给执行引擎。本文基于 StarRocks-3.4.0 源码,结合案例深入剖析优化器的规则重写机制,揭示如何通过减少无效数据计算提升查询性能。


Optimizer的核心功能

Optimizer优化SQL查询执行计划,主要通过以下两种方式:

  • 规则驱动优化(optimizeByRule):基于一系列预定义的规则重写逻辑计划。
  • 成本驱动优化(optimizeByCost):基于成本模型选择最低代价成本的物理执行计划。

在《极致性能背后的黑科技?这个世上没有“银弹”!(二)》一文中,从数据量这一角度分析StarRocks是如何让查询速度更快,简而言之,就是"越少无效的数据参与计算,越快的速度"。而本文要介绍的部分规则重写,其实也是这一思想的体现,目的就是让更个环节减少无效的数据参与计算。


Optimizer规则重写的实现

在Optimizer.java中,logicalRuleRewrite 是 StarRocks 优化器实现规则重写的核心方法,通过一系列逻辑优化规则对 Logical Plan 进行重写和优化,传进去的 tree 正是上一阶段构建好的 Logical Plan。

图片
图片

RuleSetType 枚举定义了一系列优化规则集,用于在 logicalRuleRewrite 方法中对查询计划进行重写和优化,旨在减少查询的计算开销、IO 开销和数据处理量,从而提高查询性能。

代码语言:javascript
复制
public enum RuleSetType {        LOGICAL_TRANSFORMATION,    // 逻辑计划转换        PHYSICAL_IMPLEMENTATION,   // 物理计划实现        MERGE_LIMIT,               // 合并 LIMIT 操作        PRUNE_COLUMNS,             // 列裁剪        PUSH_DOWN_PREDICATE,       // 谓词下推        SUBQUERY_REWRITE_COMMON,   // 子查询重写        ...        VECTOR_REWRITE             // 向量化重写}

案例分析

以下面2张表作为例子,建表语句及字段如下:

代码语言:javascript
复制
CREATE TABLE IF NOT EXISTS customers (        id BIGINT NOT NULL,        name VARCHAR(100),        age INT,        email VARCHAR(100)) ENGINE = olapDUPLICATE KEY (id)DISTRIBUTED BY HASH(id)PROPERTIES (        "replication_num" = "3");     CREATE TABLE IF NOT EXISTS orders (        order_id BIGINT,        customer_id INT,        order_date DATE,        amount DECIMAL(10,2)) ENGINE = olap    DUPLICATE KEY(order_id)DISTRIBUTED BY HASH(order_id)PROPERTIES (        "replication_num" = "3");

常见的重写规则

一. 谓词下推(PUSH_DOWN_PREDICATE)

谓词下推的核心思想是尽早过滤数据,避免将不必要的数据加载到计算层。通过将过滤条件下推到存储层(如文件扫描),可以在数据读取时直接跳过不符合条件的数据,减少 I/O 和计算开销。

代码语言:javascript
复制
SELECT o.order_id, o.amount, c.nameFROM orders oJOIN customers c ON o.customer_id = c.customer_idWHERE o.order_date = '2025-08-01' AND c.country = 'China';

优化前的逻辑计划树

过滤条件 o.order_date = '2025-08-01' 和 c.country = 'China' 在 Join 后应用,导致 orders 和 customers 表扫描了全表数据。

图片
图片

优化后最终的执行计划

通过Explain命令查看,将 o.order_date = '2025-08-01' 下推到 orders 表的扫描节点,c.country = 'China' 下推到 customers 表的扫描节点。扫描时只读取满足条件的行,减少了 Join 操作的输入数据量。

图片
图片

二. 列裁剪 (PRUNE_COLUMNS)

列裁剪移除查询计划中不需要的列,减少数据传输和计算开销。

代码语言:javascript
复制
SELECT order_id, customer_idFROM ordersWHERE order_date = '2025-08-01';

优化前的逻辑计划树

扫描 orders 表,读取所有列:order_id, customer_id, order_date, amount

图片
图片

优化后最终的执行计划

通过Explain命令查看,只扫描查询所需的列,最终只输出列:order_id, customer_id。减少了读取的数据量,对宽表(列数多)或数据量大的场景,列裁剪能显著减少 IO 和计算成本。这也是在SQL中尽量避免使用 "SELECT * "的原因。

图片
图片

三. LIMIT下推合并 (MERGE_LIMIT)

Limit 下推是指将查询中的 LIMIT 子句尽可能“下推”到 SCAN 阶段,从而减少后续操作需要处理的数据量;Limit Merge 是一种优化技术,用于合并多个 LIMIT 子句,避免重复计算。当一个查询中包含多个 LIMIT 子句时,StarRocks 会尝试将它们合并为一个更高效的 LIMIT。

代码语言:javascript
复制
SELECT order_id, customer_idFROM ordersWHERE order_date = '2025-08-01' LIMIT 1;

优化前的逻辑计划树

在没有 LIMIT 下推的情况下,查询会先扫描所有符合 order_date = '2025-08-01' 条件的数据,然后在最后阶段应用 LIMIT 1。

图片
图片

SCAN 节点没有 LIMIT 限制

图片
图片

优化后最终的执行计划

通过 LIMIT 下推,SCAN 在扫描数据时直接限制 LIMIT 1,当读取满足 order_date = '2025-08-01' 条件的一条数据后,就停止读取,从而减少 IO 和计算开销。

图片
图片

除上述规则外,StarRocks 还支持以下优化:

  • 分区裁剪(PARTITION_PRUNE):跳过无关分区。
  • 子查询重写(SUBQUERY_REWRITE), 优化子查询逻辑。
  • 物化视图重写(MV_REWRITE), 利用物化视图加速查询。

.......

在下一篇文章《StarRocks 查询探秘(五):Optimizer优化器之基于成本优化》中,将以一个线上外表查询计划构建频繁超时为例,深入分析 CBO的原理,敬请期待!

Optimizer 优化器是查询引擎的“大脑”,通过规则重写(RBO)和基于成本的优化(CBO),从众多执行计划中挑选最优方案,交给执行引擎。本文基于 StarRocks-3.4.0 源码,结合案例深入剖析优化器的规则重写机制,揭示如何通过减少无效数据计算提升查询性能。


Optimizer的核心功能

Optimizer优化SQL查询执行计划,主要通过以下两种方式:

  • 规则驱动优化(optimizeByRule):基于一系列预定义的规则重写逻辑计划。
  • 成本驱动优化(optimizeByCost):基于成本模型选择最低代价成本的物理执行计划。

在《极致性能背后的黑科技?这个世上没有“银弹”!(二)》一文中,从数据量这一角度分析StarRocks是如何让查询速度更快,简而言之,就是"越少无效的数据参与计算,越快的速度"。而本文要介绍的部分规则重写,其实也是这一思想的体现,目的就是让更个环节减少无效的数据参与计算。


Optimizer规则重写的实现

在Optimizer.java中,logicalRuleRewrite 是 StarRocks 优化器实现规则重写的核心方法,通过一系列逻辑优化规则对 Logical Plan 进行重写和优化,传进去的 tree 正是上一阶段构建好的 Logical Plan。

图片
图片

RuleSetType 枚举定义了一系列优化规则集,用于在 logicalRuleRewrite 方法中对查询计划进行重写和优化,旨在减少查询的计算开销、IO 开销和数据处理量,从而提高查询性能。

代码语言:javascript
复制
public enum RuleSetType {        LOGICAL_TRANSFORMATION,    // 逻辑计划转换        PHYSICAL_IMPLEMENTATION,   // 物理计划实现        MERGE_LIMIT,               // 合并 LIMIT 操作        PRUNE_COLUMNS,             // 列裁剪        PUSH_DOWN_PREDICATE,       // 谓词下推        SUBQUERY_REWRITE_COMMON,   // 子查询重写        ...        VECTOR_REWRITE             // 向量化重写}

案例分析

以下面2张表作为例子,建表语句及字段如下:

代码语言:javascript
复制
CREATE TABLE IF NOT EXISTS customers (        id BIGINT NOT NULL,        name VARCHAR(100),        age INT,        email VARCHAR(100)) ENGINE = olapDUPLICATE KEY (id)DISTRIBUTED BY HASH(id)PROPERTIES (        "replication_num" = "3");     CREATE TABLE IF NOT EXISTS orders (        order_id BIGINT,        customer_id INT,        order_date DATE,        amount DECIMAL(10,2)) ENGINE = olap    DUPLICATE KEY(order_id)DISTRIBUTED BY HASH(order_id)PROPERTIES (        "replication_num" = "3");

常见的重写规则

一. 谓词下推(PUSH_DOWN_PREDICATE)

谓词下推的核心思想是尽早过滤数据,避免将不必要的数据加载到计算层。通过将过滤条件下推到存储层(如文件扫描),可以在数据读取时直接跳过不符合条件的数据,减少 I/O 和计算开销。

代码语言:javascript
复制
SELECT o.order_id, o.amount, c.nameFROM orders oJOIN customers c ON o.customer_id = c.customer_idWHERE o.order_date = '2025-08-01' AND c.country = 'China';

优化前的逻辑计划树

过滤条件 o.order_date = '2025-08-01' 和 c.country = 'China' 在 Join 后应用,导致 orders 和 customers 表扫描了全表数据。

图片
图片

优化后最终的执行计划

通过Explain命令查看,将 o.order_date = '2025-08-01' 下推到 orders 表的扫描节点,c.country = 'China' 下推到 customers 表的扫描节点。扫描时只读取满足条件的行,减少了 Join 操作的输入数据量。

图片
图片

二. 列裁剪 (PRUNE_COLUMNS)

列裁剪移除查询计划中不需要的列,减少数据传输和计算开销。

代码语言:javascript
复制
SELECT order_id, customer_idFROM ordersWHERE order_date = '2025-08-01';

优化前的逻辑计划树

扫描 orders 表,读取所有列:order_id, customer_id, order_date, amount

图片
图片

优化后最终的执行计划

通过Explain命令查看,只扫描查询所需的列,最终只输出列:order_id, customer_id。减少了读取的数据量,对宽表(列数多)或数据量大的场景,列裁剪能显著减少 IO 和计算成本。这也是在SQL中尽量避免使用 "SELECT * "的原因。

图片
图片

三. LIMIT下推合并 (MERGE_LIMIT)

Limit 下推是指将查询中的 LIMIT 子句尽可能“下推”到 SCAN 阶段,从而减少后续操作需要处理的数据量;Limit Merge 是一种优化技术,用于合并多个 LIMIT 子句,避免重复计算。当一个查询中包含多个 LIMIT 子句时,StarRocks 会尝试将它们合并为一个更高效的 LIMIT。

代码语言:javascript
复制
SELECT order_id, customer_idFROM ordersWHERE order_date = '2025-08-01' LIMIT 1;

优化前的逻辑计划树

在没有 LIMIT 下推的情况下,查询会先扫描所有符合 order_date = '2025-08-01' 条件的数据,然后在最后阶段应用 LIMIT 1。

图片
图片

SCAN 节点没有 LIMIT 限制

图片
图片

优化后最终的执行计划

通过 LIMIT 下推,SCAN 在扫描数据时直接限制 LIMIT 1,当读取满足 order_date = '2025-08-01' 条件的一条数据后,就停止读取,从而减少 IO 和计算开销。

图片
图片

除上述规则外,StarRocks 还支持以下优化:

  • 分区裁剪(PARTITION_PRUNE):跳过无关分区。
  • 子查询重写(SUBQUERY_REWRITE), 优化子查询逻辑。
  • 物化视图重写(MV_REWRITE), 利用物化视图加速查询。

.......

在下一篇文章《StarRocks 查询探秘(五):Optimizer优化器之基于成本优化》中,将以一个线上外表查询计划构建频繁超时为例,深入分析 CBO的原理,敬请期待!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档