hive: 优化配置及bug查询路径

Hive provides two execution engines: Apache Hadoop MapReduce and Apache TEZ. Tez is faster than MapReduce.   

配置优化

tez配置优化

调整mapper

任务症状

任务中Map个数较大,超过2W甚至5W,资源紧张时任务申请不到资源;下游reduce因为上游map数量大导致shuffle慢。mapper数量与输入文件的split数息息相关,在Hadoop源码org.apache.hadoop.mapreduce.lib.input.FileInputFormat类中可以看到split划分的具体逻辑。

调优方法

set hive.execution.engine=tez;
set tez.grouping.max-size=67108864;  //如果map数过多,可以加大这个值比如*2。当前map运行时的tez.grouping.max-size就是对应于日志里面的Map 1 Split size。
set hive.tez.map.memory.mb=3072;  //beyond physical memory limits时或者oom时就可以调整map或者reduce内存大小。
"1,删除任务中的split size,使用平台默认的Split 切分算法(平台默认 Split 切分算法会考虑任务读取数据量 或 子任务的执行时间,来确定Split Size。)。
2,若需要手动调整split size,则需要打开任务运行日志链接,查看平均map运行时间。map平均运行时间以5min左右为合理,调整后map数最大不得超过50000。
tez任务调整参数:tez.grouping.max-size,单位bytes
mr任务调整参数:mapreduce.input.fileinputformat.split.maxsize或 mapred.max.split.size , 单位bytes"

tez    图示:每个map对应的total tasks,就是map个数    图示:task type对应是map时,total就是map个数

mr  可以直接通过参数mapred.map.tasks(默认值2)来设定mapper数的期望值,但它不一定会生效,下面会提到。

备注
"1,若任务确实需要使用较小的split size(如10M),则可以不开启优化(保留split size 参数即可)
2,若map存在较严重的倾斜(多个map任务超过15min以上,而平均map时间在1min以内),则可以不必调大split size"


设输入文件的总大小为total_input_size。HDFS中,一个块的大小由参数dfs.block.size指定,默认值64MB或128MB。在默认情况下,mapper数就是:
default_mapper_num = total_input_size / dfs.block.size。
参数mapred.min.split.size(默认值1B)和mapred.max.split.size(默认值64MB)分别用来指定split的最小和最大大小。split大小和split数计算规则是:
split_size = MAX(mapred.min.split.size, MIN(mapred.max.split.size, dfs.block.size));
split_num = total_input_size / split_size。
得出mapper数:
mapper_num = MIN(split_num, MAX(default_num, mapred.map.tasks))。
可见,如果想减少mapper数,就适当调高mapred.min.split.size,split数就减少了。如果想增大mapper数,除了降低mapred.min.split.size之外,也可以调高mapred.map.tasks。


一般来讲,如果输入文件是少量大文件,就减少mapper数;如果输入文件是大量非小文件,就增大mapper数;至于大量小文件的情况,得参考下面“合并小文件”一节的方法处理。

调整reducer

任务症状 reduce运行时间长

调优方法 "1,通过任务运行日志查看reduce平均运行时间,若平均reduce时间小于1min,禁止再次调大reduce个数。对于Reduce时间在1min以下的任务,需要减小reduce个数。 2,reduce个数上限为3000。大于3000个reduce的任务会变得更慢,并且增大shuffle慢风险。
设置reduce个数时,优先设置参数:set hive.exec.reducers.max=3000; 尽量不要使用mapreduce.job.reduces。"


reducer数量的确定方法比mapper简单得多。使用参数mapred.reduce.tasks可以直接设定reducer数量,不像mapper一样是期望值。但如果不设这个参数的话,Hive就会自行推测,逻辑如下:

参数hive.exec.reducers.bytes.per.reducer用来设定每个reducer能够处理的最大数据量,默认值1G(1.2版本之前)或256M(1.2版本之后)。
参数hive.exec.reducers.max用来设定每个job的最大reducer数量,默认值999(1.2版本之前)或1009(1.2版本之后)。
得出reducer数:
reducer_num = MIN(total_input_size / reducers.bytes.per.reducer, reducers.max)。
reducer数量与输出文件的数量相关。如果reducer数太多,会产生大量小文件,对HDFS造成压力。如果reducer数太少,每个reducer要处理很多数据,容易拖慢运行时间或者造成OOM。

[Optimize Apache Hive with Apache Ambari in Azure HDInsight | Microsoft Learn

大数据量的配置:

set hive.execution.engine=tez;
set tez.grouping.max-size=67108864;
set hive.tez.map.memory.mb=3072;
set hive.exec.reducers.max=3000;
set hive.tez.reduce.memory.mb=8192;
set hive.mapred.mode = 'nonstrict';
set mapreduce.task.timeout=60000000;

MapReduce优化

调整mapper数

mapper数量与输入文件的split数息息相关,在Hadoop源码org.apache.hadoop.mapreduce.lib.input.FileInputFormat类中可以看到split划分的具体逻辑。这里不贴代码,直接叙述mapper数是如何确定的。

  • 可以直接通过参数mapred.map.tasks(默认值2)来设定mapper数的期望值,但它不一定会生效,下面会提到。
  • 设输入文件的总大小为total_input_size。HDFS中,一个块的大小由参数dfs.block.size指定,默认值64MB或128MB。在默认情况下,mapper数就是: default_mapper_num = total_input_size / dfs.block.size
  • 参数mapred.min.split.size(默认值1B)和mapred.max.split.size(默认值64MB)分别用来指定split的最小和最大大小。split大小和split数计算规则是: split_size = MAX(mapred.min.split.size, MIN(mapred.max.split.size, dfs.block.size))split_num = total_input_size / split_size
  • 得出mapper数: mapper_num = MIN(split_num, MAX(default_num, mapred.map.tasks))

可见,如果想减少mapper数,就适当调高mapred.min.split.size,split数就减少了。如果想增大mapper数,除了降低mapred.min.split.size之外,也可以调高mapred.map.tasks

一般来讲,如果输入文件是少量大文件,就减少mapper数;如果输入文件是大量非小文件,就增大mapper数;至于大量小文件的情况,得参考下面“合并小文件”一节的方法处理。

调整reducer数

reducer数量的确定方法比mapper简单得多。使用参数mapred.reduce.tasks可以直接设定reducer数量,不像mapper一样是期望值。但如果不设这个参数的话,Hive就会自行推测,逻辑如下:

  • 参数hive.exec.reducers.bytes.per.reducer用来设定每个reducer能够处理的最大数据量,默认值1G(1.2版本之前)或256M(1.2版本之后)。
  • 参数hive.exec.reducers.max用来设定每个job的最大reducer数量,默认值999(1.2版本之前)或1009(1.2版本之后)。
  • 得出reducer数: reducer_num = MIN(total_input_size / reducers.bytes.per.reducer, reducers.max)

reducer数量与输出文件的数量相关。如果reducer数太多,会产生大量小文件,对HDFS造成压力。如果reducer数太少,每个reducer要处理很多数据,容易拖慢运行时间或者造成OOM。

合并小文件

  • 输入阶段合并 需要更改Hive的输入文件格式,即参数hive.input.format,默认值是org.apache.hadoop.hive.ql.io.HiveInputFormat,我们改成org.apache.hadoop.hive.ql.io.CombineHiveInputFormat。 这样比起上面调整mapper数时,又会多出两个参数,分别是mapred.min.split.size.per.nodemapred.min.split.size.per.rack,含义是单节点和单机架上的最小split大小。如果发现有split大小小于这两个值(默认都是100MB),则会进行合并。具体逻辑可以参看Hive源码中的对应类。
  • 输出阶段合并 直接将hive.merge.mapfileshive.merge.mapredfiles都设为true即可,前者表示将map-only任务的输出合并,后者表示将map-reduce任务的输出合并。 另外,hive.merge.size.per.task可以指定每个task输出后合并文件大小的期望值,hive.merge.size.smallfiles.avgsize可以指定所有输出文件大小的均值阈值,默认值都是1GB。如果平均大小不足的话,就会另外启动一个任务来进行合并。

 [Hive/HiveSQL常用优化方法全面总结 - 腾讯云开发者社区-腾讯云]

HIVE快捷查询:不启用Mapreduce job启用Fetch task

如果你想查询某个表的某一列,Hive默认是会启用MapReduce Job来完成这个任务。
启用MapReduce Job是会消耗系统开销的。
对于简单的不需要聚合的类似SELECT <col> from <table> LIMIT n语句,可以不需要起MapReduce job,直接通过Fetch task获取数据。在这种情况下,Hive可以简单地读取employee对应的存储目录下的文件,然后输出查询结果到控制台。

SET hive.fetch.task.conversion=none; 

[HIVE快捷查询 SET hive.fetch.task.conversion=none; - 简书]

调优指南

调优项目

任务症状

调优方法

tez

mr

Shuffle

任务运行波动大,怎样查看是否是因为shuffle导致。

对比两天实例,确认数据量差别不大的情况下,查看任务的平均Shuffle时间和max shuffle时间

PS: map task没有shuffle time,只有reduce task才有。
下游reduce因为上游map数量大导致shuffle慢。(好像不是这样,主要还是因为输入reduce的数据量(Task-REDUCE_INPUT_RECORDS)大,导致shuffle时间(Task-SHUFFLE_PHASE_TIME)过长。)所以可能需要看怎么减小点数据量了。

tasks:右边customize可以选择显示Task-REDUCE_INPUT_RECORDSTask-SHUFFLE_PHASE_TIME

1 Average Shuffle Time
2 entries里面:Max shuffle time(可按Elapsed time shuffle倒排)

vertex整体慢: task分批运行

containertask运行的容器。同时运行的container个数受两方面影响:同时运行的container上限(默认10000);资源不足,container满足缓慢。

1 对比两天实例,查看任务map任务start time的间隔。有两种情况:

a) task个数超过同时运行:dag并发最大4万,vertex并发默认是1万。

b) 队列资源不足,可以通过kirk查看整体的资源情况。

tasks:右边customize可以选择显示Start Time

entries里面:按Start Time排序

Vertex运行慢:数据分布出现倾斜

tasks有少量(个别)出现跑得非常慢,其它大部分跑得快得多

1 task的运行时间进行排序,存在部分task运行非常慢

2. 查看运行非常慢task与正常的task各自的counterINPUT_RECORDS,如果存在差异非常大,则是数据倾斜。

tasks:右边customize可以选择显示Task - REDUCE_INPUT_RECORDS 

Counters里面:Map input recordsReduce input records

view表时hql过滤条件优化

view表时,某些过滤条件中pt过滤条件失效,拷贝全部pt数据。如下图:

检查HQL是否会读全部pt数据,可以使用explain extended + HQL语句,查看执行计划中返回的需要读取的分区信息。如:

explain extended SELECT plat

FROM dwb.dwb_flow_ordr_impr_i_d

WHERE pt = '2021-05-12'

AND page_sn = 10004

AND page_el_sn = 4936040

AND (plat = 'iOS' OR plat = 'Android');

目前发现的有以下几种情况:HQL写错;过滤条件中包含undeterministic UDF;视图下游将分区字段和view里计算出来的字段放在一个过滤条件;下游读view,下游sql中的过滤条件下推后与view中的过滤条件矛盾,出现了always false的条件。

.HQL写错

通常时有多个过滤条件时,某一个过滤条件是用or连接,但是该过滤条件没有加括号。

二、过滤条件中包含undeterministic UDF

在这种情况下为了避免读取全部分区数据,可以将pt分区过滤条件与其他deterministic的过滤条件单独拿出来,与view表放在一起作为一个子查询,其他undeterministic的过滤条件放在子查询外面。e.g.会读全部pt分区的hql写法:

select * from t

where pt = '***' and time >= unix_timestamp('***', 'yyyy-MM-dd');

(unix_timestampundeterministic UDF)修改为以下hql,可以避免读取全部pt数据:

select * from (

select * from t

where pt = '***')t1

where time >= unix_timestamp('***', 'yyyy-MM-dd');

三、视图下游将分区字段和view里计算出来的字段放在一个过滤条件

建议用户不要在代数运算之后的列添加过滤/分区裁剪条件

四、下游读view,下游sql中的过滤条件下推后与view中的过滤条件矛盾,出现了always false的条件。

e.g.

CREATE VIEW test_view

as 

select a, b 

from test_table 

where a = 'hello';

// 下游任务

select *

from test_view 

where a = 'world'

and pt = '2222';

下游任务中的过滤条件下推到view之后,sql相当于

select a, b from test_table where a = 'hello' and a = 'world' and pt = '2222'

猜你喜欢

转载自blog.csdn.net/pipisorry/article/details/128922091