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

相关推荐
Edingbrugh.南空1 小时前
操作系统级TCP性能优化:高并发场景下的内核参数调优实践
网络协议·tcp/ip·性能优化
暗影八度1 小时前
Spark流水线数据质量检查组件
大数据·分布式·spark
Edingbrugh.南空2 小时前
ClickHouse 全生命周期性能优化
clickhouse·性能优化
运维小贺5 小时前
各服务器厂商调整BIOS睿频教程
linux·运维·服务器·性能优化
爱喝水的小周6 小时前
AJAX vs axios vs fetch
前端·javascript·ajax
Thomas_YXQ7 小时前
Unity3D游戏内存优化指南
游戏·unity·职场和发展·性能优化·蓝桥杯·游戏引擎·unity3d
合作小小程序员小小店9 小时前
web网页开发,在线%ctf管理%系统,基于html,css,webform,asp.net mvc, sqlserver, mysql
mysql·sqlserver·性能优化·asp.net·mvc
涤生大数据1 天前
Apache Spark 4.0:将大数据分析提升到新的水平
数据分析·spark·apache·数据开发
xufwind1 天前
spark standlone 集群离线安装
大数据·分布式·spark
大数据CLUB1 天前
基于spark的奥运会奖牌变化数据分析
大数据·hadoop·数据分析·spark