Spark 性能优化 (三):RBO 与 CBO

news2025/2/19 0:51:40

1. RBO 的核心概念

在 Apache Spark 的查询优化过程中,规则优化(Rule-Based Optimization, RBO) 是 Catalyst 优化器的一个关键组成部分。它主要依赖于一组固定的规则进行优化,而不是基于统计信息(如 CBO - Cost-Based Optimization)。

RBO 主要通过一系列 逻辑规则(Logical Rules)物理规则(Physical Rules) 来转换和优化查询计划。这些规则在不改变查询结果的情况下,优化查询逻辑,使查询执行得更高效。

RBO 适用于优化以下方面:

  • 谓词下推(Predicate Pushdown):减少不必要的数据扫描
  • 常量折叠(Constant Folding):减少计算量
  • 投影下推(Projection Pruning):减少数据传输
  • 消除无效操作(Eliminate Redundant Operations):去掉无用的计算

2. RBO 在 Catalyst 优化器中的角色

Spark 的 Catalyst 查询优化器由四个阶段组成:

  1. 解析(Parsing):将 SQL 语句解析成抽象语法树(AST)。
  2. 分析(Analysis):使用 Catalog 解析表和列,并检查语义错误,生成逻辑计划。
  3. 优化(Optimization)
    • 规则优化(RBO)
    • 基于代价的优化(CBO)
  4. 物理规划(Physical Planning):选择合适的执行计划(如 Hash Join、Sort Merge Join 等)。

RBO 主要在 优化阶段(Optimization) 进行,它会对逻辑计划进行一系列转换,以减少计算成本。

3. 常见的 RBO 规则

Spark 提供了大量的规则优化,以下是几个典型的 RBO 规则:

① 谓词下推(Predicate Pushdown)

WHERE 条件尽早下推到数据源,减少数据扫描量。例如:

SELECT * FROM orders WHERE order_status = 'shipped';

Spark 在 RBO 阶段会将 order_status = 'shipped' 下推到数据源层,比如 Parquet、ORC、JDBC 源等,从而减少数据扫描量。

② 常量折叠(Constant Folding)

计算可以提前执行的表达式,减少运行时计算。例如:

SELECT 1 + 2 * 3;

在 RBO 阶段,Spark 会直接优化成:

SELECT 7;

这避免了运行时计算,提升查询性能。

③ 消除无效的 LIMIT 操作(Eliminate No-op Limit)

如果 LIMIT 操作不会影响查询结果,则直接去掉。例如:

SELECT * FROM orders LIMIT 1000

如果 orders 只有 500 条记录,则 LIMIT 1000 无意义,Spark 可能会优化掉这个 LIMIT

④ 消除无效的 Sort 操作(Eliminate No-op Sort)

如果数据已经按照 ORDER BY 排序过,则去掉多余的排序。例如:

SELECT * FROM orders ORDER BY order_date

如果 orders 数据表已经按照 order_date 排序,Spark 可能会优化掉 ORDER BY 操作。

⑤ 列裁剪(Column Pruning)

减少不必要的数据传输和计算。例如:

SELECT customer_id FROM orders;

如果 orders 表有 50 列,而查询只需要 customer_id,Spark 会在 RBO 过程中移除不必要的列,减少数据扫描和传输的成本。

⑥ 过滤 NULL 值(Simplify Filters)

SELECT * FROM users WHERE age > 18 AND age IS NOT NULL;

如果 age 列设置了 NOT NULL 约束,则 age IS NOT NULL 这个条件可以去掉。

4. RBO 代码示例:

Spark 提供了 explain 方法来查看 RBO 规则应用情况:

from pyspark.sql import SparkSession

# 创建 SparkSession
spark = SparkSession.builder.appName("Spark RBO").getOrCreate()

# 创建测试 DataFrame
data = [(1, "Alice", 23), (2, "Bob", 25), (3, "Cathy", 30)]
df = spark.createDataFrame(data, ["id", "name", "age"])

# 运行查询并查看优化后的逻辑计划
df.select("id", "name").explain(mode="extended")

输出示例:

== Optimized Logical Plan ==
Project [id, name]
+- LocalRelation [id, name, age]

可以看到 Spark 在 RBO 过程中自动去掉了 age 列。

5. CBO 的核心概念

在 Apache Spark 的查询优化过程中,基于代价的优化(Cost-Based Optimization, CBO) 是 Catalyst 优化器的一个关键部分。CBO 主要依赖 统计信息(Statistics)来选择更高效的查询执行计划,相比 RBO(Rule-Based Optimization,基于规则的优化),CBO 能够更智能地优化查询性能,特别是在 Join 选择、聚合优化、谓词优化 方面。

Spark CBO 主要通过统计信息计算不同查询计划的代价(cost),并选择代价最小的执行方案。它适用于:

  • Join 重新排序(Reorder Joins)—— 选择最佳的 Join 顺序,提高执行效率。
  • 选择最佳 Join 方式(Broadcast Hash Join vs. Sort Merge Join)
  • 聚合优化(Aggregation Optimization)
  • 列裁剪优化(Column Pruning)—— 减少不必要的列传输
  • 谓词优化(Predicate Optimization)

CBO 依赖 表的统计信息,如:

  • 行数(Row Count)
  • 列的基数(Cardinality)
  • NULL 值的数量
  • 最大/最小值
  • 直方图(Histogram)

6. CBO 在 Catalyst 优化器中的作用

Spark Catalyst 查询优化器由四个阶段组成:

  1. 解析(Parsing):将 SQL 解析成抽象语法树(AST)。
  2. 分析(Analysis):使用 Catalog 解析表和列,并检查语义错误。
  3. 优化(Optimization)
    • 规则优化(RBO)——不依赖统计信息
    • 基于代价的优化(CBO)——依赖统计信息
  4. 物理计划生成(Physical Planning):选择最优执行计划。

在优化阶段,RBO 先执行,然后 CBO 基于统计信息 进一步优化查询计划,使执行更加高效。

7. Spark CBO 关键优化策略

① Join 重新排序(Reorder Joins)

CBO 通过统计信息计算不同 Join 顺序的代价,选择代价最低的 Join 执行方式。例如:

SELECT * 
FROM orders o 
JOIN customers c ON o.customer_id = c.customer_id
JOIN products p ON o.product_id = p.product_id;

如果 orders 表有 1 亿行,customers 表有 100 万行,products 表有 10 万行,理想的 Join 顺序是:

  1. 先 Join orderscustomers(较小的表优先参与 Join)
  2. 再 Join products

CBO 通过 统计信息 来决定 Join 的最佳顺序,避免大表 Join 造成的性能问题。

相关参数:

spark.sql.cbo.enabled = true  # 启用 CBO
spark.sql.cbo.joinReorder.enabled = true  # 允许 Join 重新排序

② 选择最佳 Join 方式

Spark 支持多种 Join 方式:

  • Broadcast Hash Join(适用于小表)
  • Sort Merge Join(适用于大表)
  • Shuffle Hash Join
  • Nested Loop Join(适用于非等值 Join)

CBO 通过统计信息选择合适的 Join 方式。例如,如果 customers 表小于 spark.sql.autoBroadcastJoinThreshold(默认 10MB),CBO 会选择 Broadcast Hash Join,避免 Shuffle,提高性能。

相关参数:

spark.sql.autoBroadcastJoinThreshold = 10MB  # 小于 10MB 的表自动广播

③ 聚合优化(Aggregation Optimization)

如果 CBO 发现:

  • 过滤后的数据量很小
  • 适合 Hash Aggregate(内存中完成聚合)

则 Spark 会选择 Hash Aggregate 代替 Sort Aggregate,提升查询性能。

SELECT category, COUNT(*) 
FROM products 
GROUP BY category;

如果 category 基数小,CBO 可能选择 Hash Aggregate,避免排序消耗。

④ 谓词优化(Predicate Optimization)

CBO 通过统计信息判断谓词是否能减少扫描数据量。例如:

SELECT * FROM orders WHERE order_date >= '2024-01-01';

如果 order_date 是一个 高基数列(High Cardinality),CBO 可能建议 索引扫描(Index Scan),而不是全表扫描。

⑤ 列裁剪(Column Pruning)

CBO 可以自动裁剪不必要的列,减少数据传输。例如:

SELECT customer_id FROM orders;

如果 orders 表有 50 列,而查询只涉及 customer_id,CBO 会裁剪掉其他 49 列,减少 I/O 和计算成本。

8. 如何启用 CBO

默认情况下,CBO 是 关闭 的。需要显式开启:

spark.conf.set("spark.sql.cbo.enabled", "true")
spark.conf.set("spark.sql.cbo.joinReorder.enabled", "true")  # 允许 Join 重新排序
spark.conf.set("spark.sql.statistics.histogram.enabled", "true")  # 启用直方图

此外,CBO 依赖 表的统计信息,需要手动收集:

ANALYZE TABLE orders COMPUTE STATISTICS;
ANALYZE TABLE orders COMPUTE STATISTICS FOR COLUMNS customer_id, order_date;

9. CBO 代码示例

from pyspark.sql import SparkSession

# 创建 SparkSession
spark = SparkSession.builder \
    .appName("Spark CBO") \
    .config("spark.sql.cbo.enabled", "true") \
    .config("spark.sql.cbo.joinReorder.enabled", "true") \
    .getOrCreate()

# 创建示例表
data1 = [(1, "Alice", 1000), (2, "Bob", 2000), (3, "Cathy", 3000)]
data2 = [(1, "Product A"), (2, "Product B"), (3, "Product C")]

df1 = spark.createDataFrame(data1, ["id", "name", "salary"])
df2 = spark.createDataFrame(data2, ["id", "product_name"])

# Join 并查看执行计划
df = df1.join(df2, "id")
df.explain(mode="extended")

如果统计信息已收集,CBO 会优化 Join 方式并选择最佳执行计划。

10. CBO vs. RBO

优化类型CBO(基于代价优化)RBO(规则优化)
依赖统计信息✅ 依赖统计信息❌ 不依赖统计信息
适用场景Join 选择、聚合优化、列裁剪谓词下推、常量折叠、投影下推
计算成本较高(需要计算代价)
典型优化Join 重新排序、选择最佳 Join 算法谓词下推、常量折叠

RBO 适用于基本优化,而 CBO 在大数据查询中至关重要,能够智能选择最佳执行计划。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2297746.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

读 DeepSeek-R1 论文笔记

DeepSeek-R1:通过强化学习激发大语言模型的推理能力 DeepSeek-AI 摘要 我们推出第一代推理模型DeepSeek-R1-Zero和DeepSeek-R1。DeepSeek-R1-Zero作为无需监督微调(SFT)预训练阶段、直接通过大规模强化学习(RL)训练的基础模型,展现出卓越的推理能力。…

【Android开发AI实战】选择目标跟踪基于opencv实现——运动跟踪

文章目录 【Android 开发 AI 实战】选择目标跟踪基于 opencv 实现 —— 运动跟踪一、引言二、Android 开发与 AI 的融合趋势三、OpenCV 简介四、运动跟踪原理(一)光流法(二)卡尔曼滤波(三)粒子滤波 五、基于…

如何保持长久无痛苦的学英语?

“无痛苦”学英语? 听起来像天方夜谭,但并非不可能! 关键在于,把英语学习变成你生活的一部分,融入你的兴趣和目标, 这样才能摆脱痛苦,享受学习的过程。 1. 兴趣是最好的老师: 找到自…

Sequence to Sequence model

基础模型 基础模型是用RNN模型,前部分是encoder用来寻找法语输入的编码,后半部分是decoder用来生成英文翻译作为输出,每次输出一个单词,直到输出结束标志如EOS。 下面是另一个例子,在CNN模型输出层之前会输出图片的向…

6.appender

文章目录 一、前言二、源码解析AppenderUnsynchronizedAppenderBaseOutputStreamAppenderConsoleAppenderFileAppenderRollingFileAppenderFileNamePattern 三、总结 一、前言 前一篇文章介绍了appender、conversionRule、root和logger节点的解析, 为的是为本篇详细介绍它们的…

Golang的消息队列架构

一、消息队列的定义和作用 消息队列是一种在不同组件之间传递消息的通信机制。它可以解耦系统的各个部分,提高系统的可靠性和扩展性。消息队列可以在系统之间传递消息,并且在消息发送者和消息接收者之间进行异步通信,使得系统可以更加灵活和高…

GESP5级语法知识(十一):高精度算法(一)

高精度加法&#xff1a; #include<iostream> #include<string> #include<algorithm> using namespace std; const int N501;//高精度数的最长长度 //c[]a[]b[]:高精度加法方案一&#xff1a;对应位相加&#xff0c;同时处理进位 void h_add_1(int a[],int b…

【前端】 react项目使用bootstrap、useRef和useState之间的区别和应用

一、场景描述 我想写一个轮播图的程序&#xff0c;只是把bootstrap里面的轮播图拉过来就用上感觉不是很合适&#xff0c;然后我就想自己写自动轮播&#xff0c;因此&#xff0c;这篇文章里面只是自动轮播的部分&#xff0c;没有按键跟自动轮播的衔接部分。 Ps: 本文用的是函数…

PYYAML反序列化详解

前言 最近看了很多pyyaml反序列化的漏洞利用&#xff0c;但是对漏洞怎么来的&#xff0c;没有进行很详细的分析&#xff0c;所以今天刚好学习一下反序列化的原理 Yaml基本语法 一个 .yml 文件中可以有多份配置文件&#xff0c;用 --- 隔开即可对大小写敏感YAML 中的值&#x…

LeeCode题库第十八题

项目场景&#xff1a; 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&…

Zookeeper 和 Redis 哪种更好?

目录 前言 &#xff1a; 什么是Zookeeper 和 Redis &#xff1f; 1. 核心定位与功能 2. 关键差异点 (1) 一致性模型 (2) 性能 (3) 数据容量 (4) 高可用性 3. 适用场景 使用 Zookeeper 的场景 使用 Redis 的场景 4. 替代方案 5. 如何选择&#xff1f; 6. 常见误区 7. 总结 前言…

公然上线传销项目,Web3 的底线已经被无限突破

作者&#xff1a;Techub 热点速递 撰文&#xff1a;Yangz&#xff0c;Techub News 今天早些时候&#xff0c;OKX 将上线 PI 的消息在圈内引起轩然大波&#xff0c;对于上线被板上钉钉为传销盘子的「项目」 &#xff0c;Techub News 联系了 OKX 公关&#xff0c;但对方拒绝置评…

C语言第18节:自定义类型——联合和枚举

1. 联合体 C语言中的联合体&#xff08;Union&#xff09;是一种数据结构&#xff0c;它允许在同一内存位置存储不同类型的数据。不同于结构体&#xff08;struct&#xff09;&#xff0c;结构体的成员各自占有独立的内存空间&#xff0c;而联合体的所有成员共享同一块内存区域…

解锁网络安全:穿越数字世界的防护密码

个人主页&#xff1a;java之路-CSDN博客(期待您的关注) 目录 网络安全&#xff1a;数字时代的基石 网络安全面面观 &#xff08;一&#xff09;定义与范畴 &#xff08;二&#xff09;发展历程 网络安全面临的威胁 &#xff08;一&#xff09;恶意软件肆虐 &#xff08;二…

python爬虫解决无限debugger问题

方法一 关闭定时任务 关闭断点执行代码打开断点 # 无限debugger产生原因 # 1. web开发者工具打开 # 2. js代码中有debugger # 3. js有定时处理[推荐] for(let i0;i<99999;i){window.clearInterval(i)}方法二 关闭breakpoint 方法三 修改JS代码 使用fiddler&#xff0c;抓…

C# 两种方案实现调用 DeepSeek API

目录 序 开发运行环境 访问API的一个通用方法 原生官网实现 申请 API key 调用实现 调用示例 腾讯云知识引擎原子调用 申请 API key 调用示例 小结 序 DeepSeek&#xff08;深度求索&#xff09; 最近可谓火爆的一塌糊涂&#xff0c;具体的介绍这里不再赘述&#x…

Linux下的进程切换与调度

目录 1.进程的优先级 优先级是什么 Linux下优先级的具体做法 优先级的调整为什么要受限 2.Linux下的进程切换 3.Linux下进程的调度 1.进程的优先级 我们在使用计算机的时候&#xff0c;通常会启动多个程序&#xff0c;这些程序最后都会变成进程&#xff0c;但是我们的硬…

anolis os 8.9安装jenkins

一、系统版本 # cat /etc/anolis-release Anolis OS release 8.9 二、安装 # dnf install -y epel-release # wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo # rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.…

Java基础知识总结(四十八)--TCP传输、TCP客户端、TCP服务端

**TCP传输&#xff1a;**两个端点的建立连接后会有一个传输数据的通道&#xff0c;这通道称为流&#xff0c;而且是建立在网络基础上的流&#xff0c;称之为socket流。该流中既有读取&#xff0c;也有写入。 **tcp的两个端点&#xff1a;**一个是客户端&#xff0c;一个是服务…

【python】http.server内置库构建临时文件服务

需要从linux开发机上下载一个文件到本地&#xff0c;约700M比较大&#xff0c;通过sz命令下载较慢且传输过程不稳定连续失败&#xff0c;后采用下面方式解决。 cd到一个目录下执行python -m http.server port&#xff0c;port为服务的端口号&#xff1a; 启动后浏览器中访问…