/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
一、Spark Join 策略概述
Spark作为大数据处理的利器,Join操作是其最核心也最常见的操作之一。在面对海量数据的Join场景下,选择合适的Join策略对查询性能有着决定性影响。
Spark SQL中的Join策略主要分为三类:Broadcast Hash Join(广播哈希连接)、Sort Merge Join(排序合并连接)和Shuffled Hash Join(洗牌哈希连接)。这三种策略分别针对不同规模的数据集和数据分布情况进行了优化,各具特色。
在Spark中,Join策略的选择是自动进行的,但了解其底层原理和优化机制,可以帮助我们更好地理解Spark的执行计划,并在特定场景下手动干预策略选择,从而获得更优的性能。
1.1 Join策略的基本概念
Join操作是关系型数据库和分布式计算系统中最常见的操作之一,其目的是根据两个或多个数据表中共同的关键字段,将不同表的数据关联起来,形成更大的数据集。
在Spark中,Join操作面临的主要挑战包括:
针对这些挑战,Spark设计了多种Join策略,通过不同的算法和优化手段,在不同的场景下实现最佳的查询性能。
1.2 Join策略选择的影响因素
Spark选择Join策略时,主要考虑以下因素:
理解这些因素对Join策略选择的影响,有助于我们在实际应用中进行性能调优。
二、Broadcast Hash Join 深度解析
Broadcast Hash Join(广播哈希连接)是Spark中最常用也最高效的Join策略之一,特别适合小表与大表连接的场景。
2.1 Broadcast Hash Join 的原理
Broadcast Hash Join的基本原理是将小表的数据广播到所有执行节点,每个执行节点将小表加载到内存中构建哈希表,然后在本地与大表进行Join操作,最后将结果汇总。
这种策略的核心优势在于避免了分布式数据的Shuffle操作,极大地减少了网络传输开销,同时也减少了磁盘I/O。
以下是一个简化的Broadcast Hash Join执行流程:
| Broadcast Hash Join 执行流程展示小表广播到所有节点并与大表本地Join的流程小表广播节点1节点2节点3构建哈希表大表本地Join结果1结果2结果3合并最终结果 |
2.2 Broadcast Hash Join 的适用场景
Broadcast Hash Join特别适用于以下场景:
需要注意的是,Broadcast Hash Join并不总是最优选择,例如:
2.3 Broadcast Hash Join 的配置与优化
在Spark中,可以通过以下配置优化Broadcast Hash Join:
```
spark.sql.autoBroadcastJoinThreshold = 10MB // 默认10MB
spark.sql.autoBroadcastJoinThreshold = -1 // 禁用自动广播
```
```sql
SELECT /+ BROADCAST(small_table) / *
FROM small_table JOIN large_table ON small_table.id = large_table.id
```
```
spark.sql.broadcastTimeout = 300 // 增加广播超时时间(秒)
spark.broadcast.blockSize = 2MB // 增大广播块大小
```
```
spark.sql.autoBroadcastJoinThreshold = 100MB // 提高到100MB
```
三、Sort Merge Join 深度解析
Sort Merge Join(排序合并连接)是Spark中另一种常用的Join策略,特别适合两个大表连接的场景。
3.1 Sort Merge Join 的原理
Sort Merge Join的基本原理是先将两个表按照Join键进行排序,然后在排序后的数据上进行合并操作。具体流程如下:
以下是一个简化的Sort Merge Join执行流程:
| Sort Merge Join 执行流程展示大表之间Join的Shuffle、排序与合并过程表A表BShuffleShuffle分区1分区2分区3分区1分区2分区3相同Key排序排序合并合并Join |
3.2 Sort Merge Join 的适用场景
Sort Merge Join特别适用于以下场景:
然而,Sort Merge Join也存在一些局限性:
3.3 Sort Merge Join 的配置与优化
在Spark中,可以通过以下配置优化Sort Merge Join:
```
spark.sql.join.preferSortMergeJoin = true // 优先使用Sort Merge Join
spark.sql.join.preferSortMergeJoin = false // 禁用Sort Merge Join
```
```
spark.sql.sortSpillThreshold = 10000000 // 排序溢出到磁盘的阈值
spark.sql.shuffle.partitions = 200 // 增加shuffle分区数,减少单个分区数据量
```
```
spark.sql.inMemoryColumnarStorage.compressed = true // 启用列式存储压缩
spark.sql.inMemoryColumnarStorage.batchSize = 10000 // 增大列式存储批次大小
```
```
spark.sql.adaptive.enabled = true // 启用自适应查询执行
spark.sql.adaptive.skewJoin.enabled = true // 启用倾斜Join优化
```
四、Shuffled Hash Join 深度解析
Shuffled Hash Join(洗牌哈希连接)是Spark中一种折中的Join策略,兼具了Broadcast Hash Join和Sort Merge Join的部分特点。
4.1 Shuffled Hash Join 的原理
Shuffled Hash Join的基本原理是先将两个表按照Join键进行重新分区(Shuffle),然后在每个分区上构建哈希表并执行Join操作。具体流程如下:
以下是一个简化的Shuffled Hash Join执行流程:
| Shuffled Hash Join 执行流程展示Shuffled Hash Join的分区、哈希表构建与Join过程表A表BShuffleShuffle分区1分区2分区3分区1分区2分区3相同Key构建哈希表Join操作分区Join合并结果合并 |
4.2 Shuffled Hash Join 的适用场景
Shuffled Hash Join特别适用于以下场景:
然而,Shuffled Hash Join也存在以下局限性:
4.3 Shuffled Hash Join 的配置与优化
在Spark中,可以通过以下配置优化Shuffled Hash Join:
```
spark.sql.join.preferSortMergeJoin = false // 允许使用Shuffled Hash Join
spark.sql.join.preferSortMergeJoin = true // 优先使用Sort Merge Join
```
```
spark.sql.shuffle.partitions = 200 // 增加shuffle分区数
spark.sql.adaptive.enabled = true // 启用自适应查询执行
```
```
spark.memory.fraction = 0.6 // 内存分配比例
spark.memory.storageFraction = 0.5 // 存储内存比例
```
```
spark.sql.adaptive.skewJoin.enabled = true // 启用倾斜Join优化
spark.sql.adaptive.skewJoinFactor = 5 // 倾斜因子阈值
```
五、三种Join策略的对比分析
为了更好地理解这三种Join策略的特点和适用场景,我们从多个维度进行对比分析。
5.1 性能对比
下面是三种Join策略在不同场景下的性能对比表:
| Broadcast Hash Join | 极高 | 低 | 低(仅小表广播) | 极高(广播开销) | 低(O(n+m)) |
| Sort Merge Join | 中等 | 中等 | 中等 | 高(Shuffle开销) | 中等(O(n log n + m log m)) |
| Shuffled Hash Join | 中等 | 中等 | 中等(分区内存) | 高(Shuffle开销) | 中等(O(n+m)) |
5.2 资源消耗对比
三种Join策略的资源消耗对比如下:
| 三种Join策略资源消耗对比比较Broadcast Hash Join、Sort Merge Join和Shuffled Hash Join在内存、网络和计算资源方面的消耗Broadcast Hash JoinSort Merge JoinShuffled Hash Join低高低中高中中高中内存消耗网络I/O计算复杂度 |
5.3 适用场景对比
三种Join策略的典型适用场景对比如下:
- 最佳场景:小表(小于spark.sql.autoBroadcastJoinThreshold)与大表Join
- 优势:避免Shuffle,性能最高
- 劣势:广播大表会导致严重的网络和内存压力
- 最佳场景:两个大表Join,Join键基数大
- 优势:内存使用相对稳定,不会因数据量急剧增长而崩溃
- 劣势:需要额外的排序开销,对内存有一定要求
- 最佳场景:中等规模表Join,分区数据量均衡
- 优势:相比Sort Merge Join省去排序步骤,在某些场景下性能更优
- 劣势:对内存有一定要求,数据倾斜处理能力较弱
六、Join策略优化实战指南
在实际工作中,如何选择合适的Join策略并进行优化?以下是一些实用的优化技巧和最佳实践。
6.1 自动选择与手动干预
Spark会根据表大小和配置参数自动选择Join策略,但在某些情况下,手动干预可以获得更好的性能:
```sql
SELECT /+ BROADCAST(small_table) / *
FROM small_table JOIN large_table ON small_table.id = large_table.id
```
```sql
SELECT /+ SHUFFLE_HASH(small_table) / *
FROM small_table JOIN large_table ON small_table.id = large_table.id
```
```sql
SET spark.sql.autoBroadcastJoinThreshold = -1;
```
```sql
EXPLAIN SELECT * FROM table_a JOIN table_b ON table_a.id = table_b.id
```
6.2 数据倾斜处理
数据倾斜是Join操作中最常见的问题之一,以下是几种处理方法:
```sql
— 过滤掉倾斜的键
SELECT * FROM normal_table JOIN skewed_table ON normal_table.id = skewed_table.id
WHERE skewed_table.id NOT IN (SELECT id FROM skewed_table WHERE count > threshold)
```
```sql
— 对倾斜表进行预聚合
SELECT * FROM
(SELECT id, sum(value) as total_value FROM skewed_table GROUP BY id) aggregated
JOIN normal_table ON aggregated.id = normal_table.id
```
```
spark.sql.adaptive.skewJoin.enabled = true
spark.sql.adaptive.skewJoinFactor = 5
```
```
spark.sql.shuffle.partitions = 500 // 默认通常是200
```
6.3 内存优化
Join操作对内存要求较高,以下是一些内存优化技巧:
```
spark.memory.fraction = 0.6 // 默认0.6
spark.memory.storageFraction = 0.5 // 默认0.5
```
```
spark.sql.broadcastTimeout = 300 // 默认300秒
spark.broadcast.blockSize = 2MB // 默认1MB
```
```sql
SET spark.sql.inMemoryColumnarStorage.compressed = true
SET spark.sql.inMemoryColumnarStorage.batchSize = 10000
```
```
spark.shuffle.spill.numElementsForceSpillThreshold = 1000000 // 默认1000000
spark.shuffle.spill.compress = true // 默认true
```
七、总结与最佳实践
在本文中,我们详细对比了Spark中的三种Join策略:Broadcast Hash Join、Sort Merge Join和Shuffled Hash Join。通过深入分析它们的原理、适用场景和优化方法,我们得出以下结论和建议。
7.1 核心要点回顾
7.2 性能优化建议
7.3 场景化选择建议
通过本文的学习,相信读者已经能够根据实际场景选择合适的Join策略,并进行有效的性能优化,从而提升Spark应用的查询效率。


