精选·Hive与SQL

Hive/Spark SQL 中 map join 是什么?数据倾斜的 join 怎么优化?

91学AI·2026/7/13·6 阅读

考察点

join 优化是大数据面试的压轴题之一,面试官想看你面对「任务卡在 99%」这种真实场景时的分析思路。追问方向:map join 的阈值和失败场景、加盐的具体写法、Spark AQE 能自动处理到什么程度。

参考答案

普通 join 的代价

默认的 reduce join(common join)流程:map 端读两张表,以 join key 为 shuffle key 输出,同 key 的记录进同一个 reduce,在 reduce 端做笛卡尔匹配。这个流程的问题:全量数据过一遍 shuffle,网络 IO 和磁盘 IO 都很大;而且 join key 分布不均时直接倾斜。

map join:小表广播,消灭 shuffle

map join 的思路是把小表整个加载进内存哈希表,广播到每个 map 任务,map 端读大表流式匹配,整个任务没有 reduce、没有 shuffle。大表 1TB、维表 100MB 的场景,map join 能把 join 从十几分钟压到分钟级纯扫描时间。

Hive 里:

set hive.auto.convert.join=true;          -- 自动把小表 join 转成 map join
set hive.mapjoin.smalltable.filesize=25000000;  -- 小表阈值,默认 25MB

Hive 0.11 之后默认开启自动转换,CBO 会结合统计信息判断。判断依据是文件大小而不是行数——一张 1000 行但单行特别宽的表也可能超阈值,反过来压缩后的 ORC 小文件可能装着几亿行。Spark SQL 里对应 broadcast hash join,阈值是 spark.sql.autoBroadcastJoinThreshold(默认 10MB,生产常调到 50-100MB),也可以写 hint /*+ BROADCAST(dim) */ 强制指定。

map join 的坑:小表必须真能放进内存。Driver 侧把小表读进内存建哈希表再分发,小表太大直接 OOM。另外 Hive 老版本对 outer join 做 map join 有限制(只有特定方向的小表能广播),遇到报错先查版本支持矩阵。

数据倾斜:先确认,再分类处理

倾斜的判断方法很直接:看任务的 stage/reduce 耗时分布,99% 的实例几分钟跑完,剩下几个跑几小时,拉日志看这几个实例处理的记录数,就是倾斜。用 group by join_key 数一下 top key 的行数占比,占比超过几个百分点基本坐实。

处理手段按成因分:

1. key 为 null 或空串。大量脏数据 key 是 null,全部涌向同一个 reduce。处理:过滤掉(如果这些记录不需要 join 结果),或者给 null 随机赋一个不存在的大 key concat('null_', rand()),让它们散到不同 reduce,反正匹配不上。

2. 单个热点 key。比如大 V 用户、-1 这种兜底值。Hive 有 set hive.optimize.skewjoin=true + hive.skewjoin.key(默认 10 万行判定为倾斜 key),引擎会把倾斜 key 的记录先写出去,单独起一个 map join 处理热点部分,再 union 结果。自动方案不稳的时候可以手动:

3. 加盐打散(salting)。把热点 key 拆成 N 份:

-- 大表侧:热点 key 加随机后缀
select a.*, b.dim_val
from (
  select *, concat(user_id, '_', cast(rand()*10 as int)) as join_key from big_table
) a
left join (
  -- 小表侧:热点 key 膨胀 10 倍
  select concat(user_id, '_', pos) as join_key, dim_val
  from dim_table lateral view posexplode(split(space(9), ' ')) t as pos, x
) b on a.join_key = b.join_key

大表的热点 key 被打散到 10 个 reduce,小表对应膨胀,每个 reduce 的负载降到十分之一。N 的取值按倾斜程度调,热点占比越高 N 越大。

4. 两阶段聚合(针对 group by 倾斜)。先按 key + 随机后缀 做局部聚合,再按原 key 全局聚合,把热点 key 的压力摊到多个 reduce。Spark 的 rebalance 和 AQE 的 skew join 优化(spark.sql.adaptive.skewJoin.enabled,3.x 默认开启)会自动做类似的事——AQE 检测到倾斜分区后把它切成多个子分区并行处理,还能自动把 sort merge join 降级成 broadcast join。

选型思路

join 前先看两表体量:一小一大走 map join;两大且 key 均匀走普通 reduce join / sort merge join;两大且倾斜先清洗脏 key,再上 skew join 参数或手动加盐。永远先跑 explain 确认引擎选了什么策略,别凭感觉调参。

可能的追问

  • map join 时小表多大算「小」? 没有绝对线,看单个 executor/容器的内存。经验值:加载进内存后哈希表会膨胀 3-10 倍(取决于对象开销),文件 100MB 的表内存里可能占 1GB,留足余量。
  • Hive on Spark 和原生 Spark SQL 的倾斜处理一样吗? 底层原理相同(都是 shuffle + 哈希/排序匹配),但参数体系不同。Hive 的 hive.optimize.skewjoin 在 Spark 引擎下不一定生效,Spark 侧优先用 AQE 的自动倾斜处理加手动 hint。
  • 加盐后结果会变多吗? 小表膨胀只是中间形态,join 结果集大小不变——大表每条记录只匹配膨胀后的一个副本,最终输出和正常 join 一致。

评论 (0)

暂无评论,快来抢沙发吧!

91学AI

© 2026 91学AI · 按岗位学 AI 与大数据. All rights reserved.