首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Hive 查询越来越慢?常见八大坑与优化思路

Hive 查询越来越慢?常见八大坑与优化思路

原创
作者头像
用魔法才能打败魔法
发布2025-12-02 17:56:38
发布2025-12-02 17:56:38
480
举报

前言

说实话,只要在大数据岗位干过一年以上,应该都遇到过那种离谱的 Hive 查询:昨天 3 分钟能跑完的任务,今天突然 40 分钟还卡在 map 阶段;同一个 SQL 在测试环境飞快,到了生产连日志都刷不动;有时候 Tez 跑得稀碎,一切换回 MR 又灵了…,瞬间迷茫了。

1. “为什么我的 Hive 明明没动代码,但跑着跑着越来越慢了?”

我见过最典型的三种“性能突然下降”的现场,几乎每家公司都出现过:

① map 阶段启动慢、特别慢

某次排查生产任务,我看到 map 阶段卡了 7 分钟才开始处理数据。看日志一看,全是:

代码语言:txt
复制
INFO mapreduce.JobSubmitter: number of splits: 13420

13420 个 splits?好家伙,这是在用 Hive 群发邮件吗?

结果一看 HDFS 文件,整整 1.8 亿个小文件。NameNode 的 CPU 直接干到 280%,GC 开始上天,整个任务调度都慢得要死。

性能下降不是 SQL 老化,而是小文件越来越多。

② reduce 阶段的“无限等待”

另一次,有个指标表从 3 分钟跑成 25 分钟。我盯着执行计划看了半天,发现是一个字段加了 GROUP BY,结果 reduce 全堆在一个节点上,数据倾斜严重得离谱:

  • 最大一个 reduce 拿了 82% 的数据量
  • 其他 reduce 全是休闲玩法,一秒就跑完 这就是典型的低频值 / 空值倾斜。

③ join 阶段 shuffle 暴涨

还有就是某些任务因为业务增长,数据量几个月翻几倍,但 SQL 完全没改,突然就开始狂 shuffle,网络带宽被榨干,任务能从晚上跑到天亮。

所以 Hive 性能下降并不是“程序变慢了”,而是“你看不见的东西改变了”。往往问题是:文件变多了、数据分布变了、表结构变旧了、底层引擎换了。

2. Hive on MR、Tez、Spark 到底哪个更快

我以前也天真以为 Tez 比 MR 快,Spark 比 Tez 快,这是自然规律。直到我遇到一次线上事故,让我改观。

有一次我们把 Hive on Tez 切到 Spark

结果某些任务跑得更慢了,而且慢得很均衡,不像 Tez 会卡在 DAG 的某个结点。后来才知道:

  • Spark 架构中所有 shuffle 必须落地磁盘
  • Tez 可以利用 yarn container 长驻,跨任务复用资源,减少启动开销
  • MR 虽然慢,但对超大数据、超宽表、超大 shuffle 更稳,不容易 OOM

不同引擎不是谁更牛,而是“谁更适合你的数据特征”。

真正的对比结论(实战感受):

场景

MR

Tez

Spark

小并发、大表批处理

可能更快

多 SQL 组合查询

很慢

更快

表结构复杂、宽表

稳得可怕

中等

OOM 风险大

shuffle 超大(>TB)

最稳

可能失败

很可能失败

资源紧张的集群

能跑就行

吃资源

最吃资源

我最后得到一条朴素规律:

Hive on Spark 不是性能升级,而是资源升级。

你资源够多自然快,资源不够也只能干着急。

3. 为什么大表 JOIN 大表会这么危险?

我第一次被“大表 join 大表”坑,是一个 400GB 的用户行为表和一个 700GB 的日志表。开发同学写了个 SQL:

代码语言:sql
复制
SELECT a.uid, a.action, b.ip
FROM big_a a
JOIN big_b b
ON a.uid = b.uid;

看上去很正常对吧?但这会导致:

  • 双方都要 shuffle 全量数据
  • 数据压缩后仍然几十 GB 的文件横飞
  • reduce 数量不够时直接把某个 reduce 塞爆

结果:

  • shuffle 写盘写了 700G
  • reduce 最慢的一个跑了 1 小时 40 分钟
  • 任务直接被杀掉

后来改成:

  1. 对大表预聚合
  2. 用广播小表的方式解决
  3. 或者先写成 bucket join

一般的原则是:

如果两边都大,就不要直接 join,要么缩一边,要么分桶,要么提前按 key 聚合到更小再 join。

4.数据倾斜

数据倾斜不常常体现在 code,而是体现在业务数据的不均匀分布。

WHERE 造成倾斜的经典例子

业务为了标记某些特殊用户,加了下面的代码:

代码语言:sql
复制
WHERE user_type = '0'

问题是 user_type = '0' 的数据量是另外几个类型的 30 倍。结果 map 阶段看起来正常,但 reduce(比如 join 或 group by)就一下子被拉爆。我之前遇到一个任务,map 处理 6 分钟,reduce 处理了 53 分钟,其中一个 reduce 占了全部数据的 88%。

GROUP BY倾斜

尤其是分母为零、空值、NULL 等。解决方式一般是:

  • 加盐
  • 拆分两次 group
  • 过滤掉异常空值
  • 用 map-side 聚合减少 reduce 压力

但真正的解决方式是:

去问业务数仓的人,为什么表里 80% 的用户都是 user_type='0'?

技术优化有时候不如改业务字段来得有效。

5. 小表广播(map join)

我特别喜欢 /*+ MAPJOIN(b) */ 这个提示,一键让小表不参与 shuffle,直接广播出去给所有 map 任务。

但很多人不知道,Hive 本身有 auto join 优化,它有阈值,比如:

代码语言:txt
复制
hive.auto.convert.join=true
hive.mapjoin.smalltable.filesize=25000000

默认 25MB 以下的小表会自动被广播。但问题来了:

如果表分区设计垃圾,小表也会变成大表

我们有个看着很小的 dimension 表(500MB),但因为每个分区几十 MB,自动 join 无法命中,导致它一直 shuffle。后来我把它变成一张 unpartitioned 表,直接变成 一个 500MB 的 ORC,反而速度快多了。

所以广播小表有效,但前提是:

  • 小表基础设计合理
  • 或者你明确手动标注 map join

真实有效的 SQL:

代码语言:sql
复制
SELECT /*+ MAPJOIN(dim) */ a.uid, dim.city
FROM dwd_user_log a
LEFT JOIN dim_user_city dim
ON a.uid = dim.uid;

6. ORC、Parquet和textfile

有一次我遇到一个非常魔幻的表:30TB 的数据,所有分区都是 textfile,每个表格式都是这种:

代码语言:txt
复制
字段1\t字段2\t字段3\t...

Hive 执行 plan 里根本没有谓词下推、列裁剪,map 完全要扫全表。我们把它改成 ORC 后:

  • 相同数据量
  • 执行时间从 82 分钟 → 9 分钟
  • IO 从数百 GB → 十几 GB

为什么 ORC 这么重要?

  • 支持列存
  • 支持压缩
  • 支持索引
  • 支持 predicate pushdown
  • 支持 vectorization(向量化)

通俗点说:

7. 分区设计

一次线上排查,一个任务突然从 5 分钟跑成 40 分钟。查日志发现它在 full scan,partition pushdown 根本没生效。后面查原因是开发写成了:

代码语言:sql
复制
WHERE dt = cast('2025-02-11' as string)

dt 是 string,你为什么要 cast?Hive 看到 cast,直接放弃分区裁剪。简单一句改成:

代码语言:sql
复制
WHERE dt = '2025-02-11'

任务瞬间回到 4 分钟。有些开发写 WHERE like '%2025%',更离谱。分区条件必须:

  • 明确
  • 精确匹配
  • 避免函数包裹(TO_DATE、CAST、SUBSTR 都不行)

8. 列裁剪和谓词下推

打个比方,如果你要在仓库里找一把扳手,合理的方式不是“把整个仓库搬到办公室”,而是“只把工具箱拿来”。列裁剪就是只读必要列。谓词下推就是尽可能提前过滤。

一个任务从:

代码语言:sql
复制
SELECT * FROM big_table WHERE event_type = 'login'

改成:

代码语言:sql
复制
SELECT uid, event_time FROM big_table WHERE event_type = 'login'

IO 从 280GB → 12GB,map 阶段从 18 分钟 → 1 分钟

9. Hive 参数调优

我以前也会随便写:

代码语言:txt
复制
set mapreduce.job.reduces=300;

结果把集群资源打爆,其他任务都排队。真实经验是:

reduce 数量过多会:

  • 创建大量文件(小文件地狱)
  • 占用大量 container
  • shuffle 时间爆炸

reduce 数量过少会:

  • 单个 reduce 被塞爆
  • 出现数据倾斜
  • OOM

所以我总结了以下使用规律:

  • 10GB 数据量 → 1~3 个 reduce
  • 100GB → 5~20 个 reduce
  • 1TB → 20~50 个 reduce

但这取决于你的 key 分布。map 数量不用太操心,HDFS splits 决定的,你要调的是“不要有太多小文件”。另外我常用的几个参数:

代码语言:txt
复制
set hive.exec.reducers.bytes.per.reducer=512000000;  -- 每个 reduce 处理 512MB
set hive.optimize.skewjoin=true;                     -- 自动倾斜处理
set hive.groupby.skewindata=true;                    -- group by 倾斜优化
set hive.exec.dynamic.partition.mode=nonstrict;

10. 压缩格式

我曾经遇到一个 400GB 的 gzip 文件,解压用了 17 分钟,map 阶段就 17 分钟。换成 snappy 后,同样条件 3~4 分钟搞定。

格式

压缩比

解压速度

适用场景

snappy

快到离谱

Hive 主力格式

gzip

离线冷数据、归档数据

lzo

历史遗留项目

11. 小文件

我们公司有个任务,QPS 正常,Hive 任务也正常,但 NameNode 每天凌晨 CPU 都打满。最后定位到一个日志任务,每天生成 2000 万个小文件。NameNode 的问题是:

  • 元数据放内存
  • 文件数太多会让 fsimage 超大
  • editlog 也膨胀
  • GC 频繁(我见过一秒三次)
  • RPC 响应延迟变大
  • Hive 任务明显变慢

我见过 NameNode 因为小文件太多,重启耗时 47 分钟才恢复。

12. 真实优化案例

有一次,一个实时 T+1 的指标任务,从稳定 3 分钟跑完 → 突然 30 分钟。当时对于这个问题的的排查步骤如下:

第 1 步:看执行计划

结果看到 join 阶段 shuffle 量从几百 MB → 20GB。

第 2 步:查明是谁变大了

发现 dim_city 表从 200MB → 6GB。开发加了几个 from_app、from_channel 字段,变胖了。

第 3 步:看是否命中广播 join

没有命中,因为太大了。

第 4 步:手动用 map join 强制广播

改 SQL:

代码语言:sql
复制
SELECT /*+ MAPJOIN(dim) */ a.uid, dim.city
FROM dwd_user_log a
LEFT JOIN dim_city dim
ON a.city_id = dim.city_id;

变成 3 分钟。

第 5 步:优化表结构

让 dim_city 只保留需要的字段,删掉无关字段后变回 250MB。

第 6 步:调小 reduce

从默认的 20 个 reduce → 8 个,避免小文件爆炸。最终任务从 30 分钟回到 3 分钟。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 1. “为什么我的 Hive 明明没动代码,但跑着跑着越来越慢了?”
    • ① map 阶段启动慢、特别慢
    • ② reduce 阶段的“无限等待”
    • ③ join 阶段 shuffle 暴涨
  • 2. Hive on MR、Tez、Spark 到底哪个更快
    • 有一次我们把 Hive on Tez 切到 Spark
    • 真正的对比结论(实战感受):
  • 3. 为什么大表 JOIN 大表会这么危险?
  • 4.数据倾斜
    • WHERE 造成倾斜的经典例子
    • GROUP BY倾斜
  • 5. 小表广播(map join)
    • 如果表分区设计垃圾,小表也会变成大表
  • 6. ORC、Parquet和textfile
  • 7. 分区设计
  • 8. 列裁剪和谓词下推
  • 9. Hive 参数调优
    • reduce 数量过多会:
    • reduce 数量过少会:
  • 10. 压缩格式
  • 11. 小文件
  • 12. 真实优化案例
    • 第 1 步:看执行计划
    • 第 2 步:查明是谁变大了
    • 第 3 步:看是否命中广播 join
    • 第 4 步:手动用 map join 强制广播
    • 第 5 步:优化表结构
    • 第 6 步:调小 reduce
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档