在工作中对 hive 做过哪些优化
错误示范(枯最后是 SQL 语法层面的优化燥干瘪版)
我做过的 Hive 优化挺多的,比如用分区表、分桶表,开谓词下推,合并小文件,把 MR 引擎换成 Tez 或者 Spark 引擎,处理数据倾斜,调大并行度,用 mapjoin,开 CBO 优化器,还有 SQL 层面尽量不用 select *,先过滤再关联这些。
正确回答(叙事场景版)
之前对一个大促活动, 在促前做全链路压测,发现核心的用户行为宽表生成任务跑了 3 个多小时还没跑完,下游的交易、流量报表都等着这个表出数,要是大促期间也这个速度,业务方肯定要投诉,所以当时牵头做了全链路的 Hive 优化,核心动作都是跟着实际问题走的:
- 首先排查输入层的问题,发现 ODS 层的用户行为数据是每 5 分钟从 Kafka 同步一次,一天下来有 2200 多个小文件,每个才 30-50KB,Hive 读取的时候大量时间浪费在申请资源、建立连接上。我们当时改了两个配置:一是开了
CombineHiveInputFormat合并小文件作为输入,二是给 ODS 层同步任务加了后置的小文件合并脚本,每次同步完成后把小于 128M 的文件合并成 128M 的标准块,就这一步,任务的输入读取时间直接从 42 分钟降到了 7 分钟。 - 然后把原来默认的 MR 引擎换成了 Tez 引擎,同时开了向量化执行和 CBO 优化器,也就是配置
hive.vectorized.execution.enabled=true、hive.cbo.enable=true,Tez 是基于有向无环图的执行引擎,不用像 MR 那样每次阶段执行完都落盘,这一步直接把整体任务的执行效率提了 35% 左右。 - 接着是存储层的优化,原来的用户行为表是普通的非分区表,每次查询都要扫全表,我们改成了按 dt 作为一级分区、小时作为二级分区的分区表,同时按 user_id 分了 32 个桶,后面做表关联的时候,比如和用户维度表关联,因为两个表都是按 user_id 分桶的,直接做桶内关联,不用 shuffle 全量数据,原来关联阶段要跑 22 分钟,改完之后只需要 6 分钟。
- 后来跑的时候又遇到了数据倾斜的问题,统计用户下单行为的时候,有个 reduce 任务卡了 1 个半小时还没跑完,查了日志发现是大量未登录用户的 user_id 是空值,全部跑到同一个 reduce 里去了。我们当时做了两个处理:一是把空值的 user_id 改成随机前缀的字符串,分散到不同的 reduce 处理,二是开了
hive.optimize.skewjoin=true的参数,自动检测倾斜的 key 做拆分处理,这一步直接把卡了很久的 reduce 阶段降到了 9 分钟。 - ,原来的开发同学写 SQL 图省事直接写 select *,我们改成只取需要的 17 个字段,同时把过滤条件提前到子查询里,先过滤再做关联,还有关联 10M 以内的用户维度表的时候,手动加
/*+ MAPJOIN(dim_user) */的 hint,把小表提前加载到内存里做 map 端 join,不用走 shuffle。
最后整个用户行为宽表的生成任务,从原来的 3 小时 18 分钟,优化到了 26 分钟,618 大促当天峰值数据也只跑了 38 分钟,完全满足业务方 T+1 出数的要求。后来我还把这些优化的规则、配置、规范整理成了团队的 Hive 开发手册,新人入职先学这个,后续再也没出现过 Hive 任务超时影响报表出数的问题。