优化
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