应避免在where条件中对分区字段使用函数,否则hive无法下推过滤导致全表扫描;正确做法是用分区字段直接比较以实现分区裁剪,并注意类型一致、压缩和NULL值等隐性性能陷阱。

WHERE 条件里用函数导致全表扫描
Hive 会把 WHERE substr(dt, 1, 7) = '2024-01' 这类带函数的条件,下推不到底层 map 阶段做过滤,结果是所有分区都读进来再计算,哪怕你只想要一个分区的数据。
正确做法是让分区字段本身参与比较,Hive 才能自动裁剪分区:
- 确保分区字段(如
dt)是字符串类型且格式规范(如'2024-01-01') - 改写为
WHERE dt >= '2024-01-01' AND dt ,或更安全的 <code>WHERE dt LIKE '2024-01%' - 如果原始数据里
dt是INT或BIGINT(如20240101),就别转成字符串再截取,直接用数值范围:WHERE dt BETWEEN 20240101 AND 20240131
JOIN 大表没走 MapJoin 导致 Reduce OOM
当小表(比如维表)超过默认阈值(hive.mapjoin.smalltable.filesize,默认 25MB),Hive 就会退化为普通 Reduce Join,容易在 reduce 阶段内存溢出。
实操上得主动干预:
- 手动开启 MapJoin:加提示
/*+ MAPJOIN(dim_user) */,但前提是dim_user真的小(压缩后 - 检查小表实际大小:
hdfs dfs -du -h /user/hive/warehouse/dim_user,注意看压缩后字节数,不是源文件大小 - 如果小表含大量 NULL 值,记得加
SET hive.optimize.null.scan=true,避免 NULL 导致 MapJoin 失效
GROUP BY 字段太多引发数据倾斜
像 GROUP BY user_id, item_id, category_id, os_version, device_id 这种组合,一旦某类设备(比如某个测试机 device_id)打点异常高频,reduce task 就会卡死,日志里常见 java.lang.OutOfMemoryError: Java heap space 或长时间无进度。
拆解思路不是删字段,而是分层聚合:
- 先按高基数字段(如
user_id)粗粒度聚合一次,生成中间表 - 再对中间表按完整维度二次聚合,减少单次 shuffle 的 key 总量
- 或者给 key 加随机前缀打散:
CONCAT(CAST(RAND() * 10 AS INT), '_', user_id),但要注意后续要去重或加权还原
SELECT * 拉取大宽表触发 YARN Container 被杀
Hive 默认不校验 SELECT 列数和宽度,但 YARN 容器有内存上限。一旦宽表(比如 200+ 列、含多个 STRING 大字段)被全拉,单个 mapper 可能申请超 4GB 内存,YARN 直接 kill container,报错 Container killed by YARN for exceeding memory limits。
必须显式约束输出:
- 永远不要在生产查询里写
SELECT *,哪怕只是临时查;列名全写出来,顺便确认哪些可以CAST缩容(比如CAST(event_time AS STRING)比原生TIMESTAMP更省序列化开销) - 对大文本字段(如
log_body)加SUBSTR(log_body, 1, 500)截断,除非真要全文分析 - 用
EXPLAIN EXTENDED看执行计划里Stage-1的 output size 预估,> 1GB 就该警惕
复杂点往往藏在“看起来没问题”的地方:分区字段类型不一致、小表实际没压缩、NULL 值干扰优化器判断——这些不会报错,但会让性能掉一个数量级。