当前位置: 首页 > article >正文

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 查询优化器由四个阶段组成:

  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 在大数据查询中至关重要,能够智能选择最佳执行计划。

相关文章:

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级语法知识(十一):高精度算法(一)

高精度加法&#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…...

【离散数学上机】T235,T236

T235题目&#xff1a;输入集合A和B&#xff0c;输出A到B上的所有单射函数。 问题描述 给定非空数字集合A和B&#xff0c;求出集合A到集合B上的所有单射函数。 输入格式 第一行输入m和n&#xff08;空格间隔&#xff09;&#xff0c;分别为集合A和集合B中的元素个数&#xff1b;…...

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. 总结 前言…...

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_localtime 函数

ngx_localtime 函数 声明 在 src\os\unix\ngx_time.h 中&#xff1a; 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 中&#xff0c;初始化方法通常是在应用程序启动时被调用的&#xff0c;可以用来执行应用启动时的一些准备工作。以下是几种常见的初始化方法&#xff1a; 一、顺序 1. 图解 ┌─────────────────────────────┐│ Spring Boot…...

vue组件中各种类型之间的传值

在Vue CLI项目中&#xff0c;组件间的属性传值是一个常见的需求。以下是一些常用的传值方法和规范&#xff0c;以及相应的代码演示和解说&#xff1a; 一. 父组件向子组件传值&#xff08;Props&#xff09; 规范&#xff1a;父组件通过属性&#xff08;props&#xff09;向子…...

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

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

GitLab CI/CD 的配置详解:从零开始使用 .gitlab-ci.yml 文件

在现代软件开发中&#xff0c;CI/CD&#xff08;持续集成与持续部署&#xff09;已成为提高开发效率和代码质量的核心实践。GitLab CI/CD 提供了强大的功能&#xff0c;帮助开发者自动化构建、测试和部署应用程序。而 .gitlab-ci.yml 文件是 GitLab CI/CD 配置的关键所在&#…...

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

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

Python的元组和列表的区别是什么?

1. 定义和语法形式 列表&#xff08;List&#xff09;&#xff1a;列表是一种可变的序列类型&#xff0c;使用方括号 [] 来定义。例如&#xff1a;my_list [1, 2, 3] 。列表中的元素可以是不同的数据类型&#xff0c;并且可以包含嵌套的列表、元组等其他数据结构。元组&#x…...

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

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