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

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

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

1. “为什么我的 Hive 明明没动代码,但跑着跑着越来越慢了?”我见过最典型的三种“性能突然下降”的现场,几乎每家公司都出现过:

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

代码语言:txt复制INFO mapreduce.JobSubmitter: number of splits: 1342013420 个 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 写盘写了 700Greduce 最慢的一个跑了 1 小时 40 分钟任务直接被杀掉后来改成:

对大表预聚合用广播小表的方式解决或者先写成 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 数量过多会:创建大量文件(小文件地狱)占用大量 containershuffle 时间爆炸reduce 数量过少会:单个 reduce 被塞爆出现数据倾斜OOM所以我总结了以下使用规律:

10GB 数据量 → 1~3 个 reduce100GB → 5~20 个 reduce1TB → 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 分钟。

相关推荐

【瑞】字的词语大全
365体育app官方版下载

【瑞】字的词语大全

📅 02-18 👁️ 6197
《如懿传》阿箬为何不供出主谋,杀她兄弟难道比诛九族更可怕
首字母是czy的网名 流行43个
365体育app官方版下载

首字母是czy的网名 流行43个

📅 08-05 👁️ 3735