Spark 性能优化 (三):RBO 与 CBO
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 查询优化器由四个阶段组成:
- 解析(Parsing):将 SQL 语句解析成抽象语法树(AST)。
- 分析(Analysis):使用 Catalog 解析表和列,并检查语义错误,生成逻辑计划。
- 优化(Optimization):
- 规则优化(RBO)
- 基于代价的优化(CBO)
- 物理规划(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 查询优化器由四个阶段组成:
- 解析(Parsing):将 SQL 解析成抽象语法树(AST)。
- 分析(Analysis):使用 Catalog 解析表和列,并检查语义错误。
- 优化(Optimization):
- 规则优化(RBO)——不依赖统计信息
- 基于代价的优化(CBO)——依赖统计信息
- 物理计划生成(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 顺序是:
- 先 Join
orders和customers(较小的表优先参与 Join) - 再 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 在大数据查询中至关重要,能够智能选择最佳执行计划。
相关文章:
Spark 性能优化 (三):RBO 与 CBO
1. RBO 的核心概念 在 Apache Spark 的查询优化过程中,规则优化(Rule-Based Optimization, RBO) 是 Catalyst 优化器的一个关键组成部分。它主要依赖于一组固定的规则进行优化,而不是基于统计信息(如 CBO - Cost-Base…...
读 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 简介四、运动跟踪原理(一)光流法(二)卡尔曼滤波(三)粒子滤波 五、基于…...
Eclipse JSP/Servlet 深入解析
Eclipse JSP/Servlet 深入解析 引言 随着互联网的快速发展,Java Web开发技术逐渐成为企业级应用开发的主流。在Java Web开发中,JSP(JavaServer Pages)和Servlet是两个核心组件,它们共同构成了Java Web应用程序的基础。本文将深入解析Eclipse平台下的JSP/Servlet技术,帮…...
申论概括类【2021副省第二题“局区合一”】
材料: “李总监,您好,我是芯谷产业功能区项目投资科的小罗,从今天开始,我就是你们公司的项目专员,以后有什么问题您都可以找我。”W光学有限公司总务部总监李晓枫接到小罗的电话时,既意外又暖心…...
如何保持长久无痛苦的学英语?
“无痛苦”学英语? 听起来像天方夜谭,但并非不可能! 关键在于,把英语学习变成你生活的一部分,融入你的兴趣和目标, 这样才能摆脱痛苦,享受学习的过程。 1. 兴趣是最好的老师: 找到自…...
SQL-leetcode—1661. 每台机器的进程平均运行时间
1661. 每台机器的进程平均运行时间 表: Activity ----------------------- | Column Name | Type | ----------------------- | machine_id | int | | process_id | int | | activity_type | enum | | timestamp | float | ----------------------- 该表展示了一家工厂网站的…...
Linux例行任务:at 、cron、 /etc/contain 辨析
文章目录 一、at:一次性任务调度1. **基本用法**2. **管理任务**3. **权限控制** 二、cron:周期性任务调度1. **用户级任务**2. **系统级任务**3. **特殊字符串**4. **权限控制**5. **环境问题** 三、容器环境中的例行任务1. **在容器内运行 cron**2. **…...
Vue2中常用指令
文章目录 Vue2中常用指令1. v-text 动态渲染纯文本内容1. 作用2. 基本用法3. 示例4. 注意事项 2. v-html 动态渲染 HTML 内容1. 作用2. 基本用法3. 示例4. 注意事项 3. v-bind 动态绑定 HTML 属性1. 作用2. 基本用法3. 示例4. 注意事项5. 绑定class属性的用法6. 绑定style属性的…...
Sequence to Sequence model
基础模型 基础模型是用RNN模型,前部分是encoder用来寻找法语输入的编码,后半部分是decoder用来生成英文翻译作为输出,每次输出一个单词,直到输出结束标志如EOS。 下面是另一个例子,在CNN模型输出层之前会输出图片的向…...
PHP 超级全局变量
PHP 超级全局变量 引言 在PHP编程中,超级全局变量(Superglobals)是一类特殊的变量,它们在任何函数、类或文件中都可以访问。这些变量在PHP的全局作用域中始终可用,为开发者提供了处理HTTP请求和响应的强大工具。本文…...
如何在Vscode中接入Deepseek
在VS Code(Visual Studio Code)中接入DeepSeek,可以按照以下步骤进行操作: 一、准备工作 确保VS Code为最新版本: DeepSeek可能依赖于VS Code的某些最新功能或修复,因此建议先将VS Code更新到最新版本。注…...
6.appender
文章目录 一、前言二、源码解析AppenderUnsynchronizedAppenderBaseOutputStreamAppenderConsoleAppenderFileAppenderRollingFileAppenderFileNamePattern 三、总结 一、前言 前一篇文章介绍了appender、conversionRule、root和logger节点的解析, 为的是为本篇详细介绍它们的…...
Golang的消息队列架构
一、消息队列的定义和作用 消息队列是一种在不同组件之间传递消息的通信机制。它可以解耦系统的各个部分,提高系统的可靠性和扩展性。消息队列可以在系统之间传递消息,并且在消息发送者和消息接收者之间进行异步通信,使得系统可以更加灵活和高…...
如何在Servlet容器中使用HttpServletResponse?
HttpServletResponse 是 Java Servlet API 中的一个接口,它代表了服务器对客户端的响应。通过 HttpServletResponse 对象,可以设置响应的状态码、发送数据到客户端(如 HTML 页面、文件等)、添加响应头信息等。下面是如何在 Servle…...
DeepSeek自然语言处理(NLP)基础与实践
自然语言处理(Natural Language Processing, NLP)是人工智能领域的一个重要分支,专注于让计算机理解、生成和处理人类语言。NLP技术广泛应用于机器翻译、情感分析、文本分类、问答系统等场景。DeepSeek提供了强大的工具和API,帮助我们高效地构建和训练NLP模型。本文将详细介…...
GESP5级语法知识(十一):高精度算法(一)
高精度加法: #include<iostream> #include<string> #include<algorithm> using namespace std; const int N501;//高精度数的最长长度 //c[]a[]b[]:高精度加法方案一:对应位相加,同时处理进位 void h_add_1(int a[],int b…...
【前端】 react项目使用bootstrap、useRef和useState之间的区别和应用
一、场景描述 我想写一个轮播图的程序,只是把bootstrap里面的轮播图拉过来就用上感觉不是很合适,然后我就想自己写自动轮播,因此,这篇文章里面只是自动轮播的部分,没有按键跟自动轮播的衔接部分。 Ps: 本文用的是函数…...
PYYAML反序列化详解
前言 最近看了很多pyyaml反序列化的漏洞利用,但是对漏洞怎么来的,没有进行很详细的分析,所以今天刚好学习一下反序列化的原理 Yaml基本语法 一个 .yml 文件中可以有多份配置文件,用 --- 隔开即可对大小写敏感YAML 中的值&#x…...
【离散数学上机】T235,T236
T235题目:输入集合A和B,输出A到B上的所有单射函数。 问题描述 给定非空数字集合A和B,求出集合A到集合B上的所有单射函数。 输入格式 第一行输入m和n(空格间隔),分别为集合A和集合B中的元素个数;…...
LeeCode题库第十八题
项目场景: 给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复)&…...
Zookeeper 和 Redis 哪种更好?
目录 前言 : 什么是Zookeeper 和 Redis ? 1. 核心定位与功能 2. 关键差异点 (1) 一致性模型 (2) 性能 (3) 数据容量 (4) 高可用性 3. 适用场景 使用 Zookeeper 的场景 使用 Redis 的场景 4. 替代方案 5. 如何选择? 6. 常见误区 7. 总结 前言…...
Ubuntu 下 nginx-1.24.0 源码分析 - ngx_localtime 函数
ngx_localtime 函数 声明 在 src\os\unix\ngx_time.h 中: void ngx_localtime(time_t s, ngx_tm_t *tm); 定义 在 src/os/unix/ngx_time.c 中 void ngx_localtime(time_t s, ngx_tm_t *tm) { #if (NGX_HAVE_LOCALTIME_R)(void) localtime_r(&s, tm);#elsengx_tm…...
SpringBoot初始化8个常用方法
在 Spring Boot 中,初始化方法通常是在应用程序启动时被调用的,可以用来执行应用启动时的一些准备工作。以下是几种常见的初始化方法: 一、顺序 1. 图解 ┌─────────────────────────────┐│ Spring Boot…...
vue组件中各种类型之间的传值
在Vue CLI项目中,组件间的属性传值是一个常见的需求。以下是一些常用的传值方法和规范,以及相应的代码演示和解说: 一. 父组件向子组件传值(Props) 规范:父组件通过属性(props)向子…...
公然上线传销项目,Web3 的底线已经被无限突破
作者:Techub 热点速递 撰文:Yangz,Techub News 今天早些时候,OKX 将上线 PI 的消息在圈内引起轩然大波,对于上线被板上钉钉为传销盘子的「项目」 ,Techub News 联系了 OKX 公关,但对方拒绝置评…...
GitLab CI/CD 的配置详解:从零开始使用 .gitlab-ci.yml 文件
在现代软件开发中,CI/CD(持续集成与持续部署)已成为提高开发效率和代码质量的核心实践。GitLab CI/CD 提供了强大的功能,帮助开发者自动化构建、测试和部署应用程序。而 .gitlab-ci.yml 文件是 GitLab CI/CD 配置的关键所在&#…...
C语言第18节:自定义类型——联合和枚举
1. 联合体 C语言中的联合体(Union)是一种数据结构,它允许在同一内存位置存储不同类型的数据。不同于结构体(struct),结构体的成员各自占有独立的内存空间,而联合体的所有成员共享同一块内存区域…...
Python的元组和列表的区别是什么?
1. 定义和语法形式 列表(List):列表是一种可变的序列类型,使用方括号 [] 来定义。例如:my_list [1, 2, 3] 。列表中的元素可以是不同的数据类型,并且可以包含嵌套的列表、元组等其他数据结构。元组&#x…...
解锁网络安全:穿越数字世界的防护密码
个人主页:java之路-CSDN博客(期待您的关注) 目录 网络安全:数字时代的基石 网络安全面面观 (一)定义与范畴 (二)发展历程 网络安全面临的威胁 (一)恶意软件肆虐 (二…...
