优化

Hive SQL 优化:慢 SQL 排查路径 + 对应的优化手段

一条 Hive SQL 跑得慢,原因可能出在任何一个环节——全表扫描、小文件太多、Join 数据倾斜、Reduce 数量不对、内存不够。

优化的前提是先定位问题。排查路径是:

EXPLAIN 看执行计划 → 看有没有全表扫描/不合理 Join → 看数据分布是否倾斜 → 调参数

先看执行计划:EXPLAIN

优化之前,先用 EXPLAIN 看 Hive 打算怎么跑这条 SQL。

EXPLAIN SELECT max(sal), deptno FROM emp GROUP BY deptno;

执行计划里重点关注:

看什么 说明
阶段依赖 Stage 之间的顺序,看有没有不必要的依赖
操作符树 TableScan → Filter → GroupBy → …,看有没有全表扫描
是否有 Reduce 阶段 有些查询根本不需要 Reduce(比如 SELECT *),强行走 MR 就慢了
分区过滤 分区表有没有走分区过滤

EXPLAIN 能告诉你”Hive 打算怎么执行”,然后你才能判断”这个计划有没有问题”。

现象1:全表扫描 → 加分区过滤 + 列裁剪

现象: 执行计划里出现 TableScan 扫了整张表,Filter 条件里没有分区列。

原因: 查询时没带分区条件,或者分区条件写错了。

解决:

-- 错误: 全表扫描
SELECT * FROM orders WHERE amount > 100;

-- 优化:加上分区条件
SELECT * FROM orders WHERE dt='2026-07-01' AND amount > 100;

--  优化:只查需要的列,不要 SELECT *
SELECT order_id, amount FROM orders WHERE dt='2026-07-01';

相关配置:

-- 开启 Fetch 抓取,简单查询不走 MR
SET hive.fetch.task.conversion=more;

现象2:小数据量也跑 MR → 开本地模式

现象: 数据量只有几 MB,但任务还是要启动 MapReduce,光启动就花了 30 秒。

原因: Hive 默认不管数据大小都走 MR。

解决: 开启本地模式,小数据直接在单节点处理。

SET hive.exec.mode.local.auto=true;
SET hive.exec.mode.local.auto.inputbytes.max=134217728;  -- 128MB

现象3:Join 慢 → 小表走 MapJoin,大表处理倾斜

现象: Join 阶段特别慢,Reduce 卡在 99% 不动。

原因1:小表 Join 大表,但没走 MapJoin

小表数据量不大(< 25MB),完全可以加载到内存里做 MapJoin,避免 Shuffle。

-- 自动开启 MapJoin
SET hive.auto.convert.join=true;
SET hive.mapjoin.smalltable.filesize=25000000;  -- 25MB

原因2:大表 Join 大表,数据倾斜

某个 Join Key 的数据量特别大(比如 user_id='guest' 有 1 亿条),全挤到一个 Reduce 任务。

解决方案:

① 开启倾斜 Join 自动优化

SET hive.optimize.skewjoin=true;
SET hive.skewjoin.key=100000;

② 手动加盐分散

-- 给倾斜 key 加随机前缀,分散到多个 Reduce
SELECT /*+ SKEWJOIN('orders') */
  t1.user_id, t2.order_id
FROM users t1
JOIN orders t2
ON CASE WHEN t1.user_id IN ('hot_id1', 'hot_id2')
        THEN concat(cast(rand()*10 as int), '_', t1.user_id)
        ELSE t1.user_id END = t2.user_id;

原因3:Join 顺序不对

Hive 从右到左执行 Join,大表放右边可以减少中间数据量。

-- 错误: 大表在左
SELECT * FROM big_table JOIN small_table ON ...;

-- 正确: 大表在右
SELECT * FROM small_table JOIN big_table ON ...;

现象4:Group By 慢 → Map 端预聚合 + 处理倾斜

现象: GROUP BY 阶段卡住,某个 Reduce 处理的数据量远超其他。

原因1:没有开启 Map 端聚合

所有数据直接 Shuffle 到 Reduce,没有在 Map 端做预聚合。

SET hive.map.aggr=true;

原因2:分组键倾斜

某个分组值的数据量特别大(比如 city='北京' 有 1000 万条)。

解决: 加盐两次聚合

-- 第一步:加盐局部聚合
SELECT substr(city, 2) AS city, SUM(amount) AS total
FROM (
  SELECT concat(cast(rand()*10 as int), '_', city) AS city, amount
  FROM orders
) t
GROUP BY city;

-- 第二步:去盐全局聚合
SELECT city, SUM(total) AS final_total
FROM 上一步结果
GROUP BY city;

现象5:任务整体慢 → 调并行度和 Reduce 数量

现象: 任务耗时很长,但看执行计划发现多个 Stage 是独立的,串行执行浪费时间。

解决:开并行执行

SET hive.exec.parallel=true;
SET hive.exec.parallel.thread.number=8;

现象: Reduce 阶段太慢,或者 Reduce 数量不合理。

Reduce 数量的决定因素:

Reduce 数 = 输入数据量 / hive.exec.reducers.bytes.per.reducer

调小 bytes.per.reducer → Reduce 数量增多 → 并行度提高
调大 bytes.per.reducer → Reduce 数量减少 → 小文件减少

-- 调整每个 Reduce 处理的数据量(默认 256MB)
SET hive.exec.reducers.bytes.per.reducer=128000000;  -- 128MB