因为Hive底层就是MR,所以问题实际是MR如何确定map数量和reduce数量.

map数量

map数量 逻辑如下

  • map数量=split数量
  • split数量=文件大小/split size
  • splitszie=Math.max(minSize, Math.min(maxSize, blockSize))

如果不改,用默认值的话,split size就是128M.
默认参数如下

hive> set mapred.min.split.size;
mapred.min.split.size=1
hive> set mapred.max.split.size;
mapred.max.split.size=256000000
hive> set dfs.block.size;
dfs.block.size=134217728

举个例子

a)    假设input目录下有1个文件a,大小为780M,那么hadoop会将该文件a分隔成7个块(6个128m的块和1个12m的块),从而产生7个map数
b)    假设input目录下有3个文件a,b,c,大小分别为10m,20m,130m,那么hadoop会分隔成4个块(10m,20m,128m,2m),从而产生4个map数
即,如果文件大于块大小(128m),那么会拆分,如果小于块大小,则把该文件当成一个块。

map数不是越多越好

如果有很多小文件,每个小文件被当作一个块,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,这就不合理了. 属于资源浪费.

解决这个,可以对小文件进行合并处理.
举个例子

select count(1) from emp where dt='2020-9-1'

然后这个目录下有200个1m左右的小文件,那么就需要200个task.

-- 每个map最大输入大小
set mapred.max.split.size=100000000;
--节点中可以处理的最小文件大小,100M
set mapred.min.split.size.per.node=100000000;
--机架中可以处理的最小的文件大小
set mapred.min.split.size.per.rack=100000000;
//表示执行map前对小文件进行合并
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

ps,这些参数都可以在hive的命令行界面直接查看!不需要找配置文件,比如

mapred.max.split.size=256000000
hive> set mapred.max.split.size;
mapred.max.split.size=256000000
hive> set mapred.min.split.size.per.node;
mapred.min.split.size.per.node=1
hive> set mapred.min.split.size.per.rack;
mapred.min.split.size.per.rack=1
hive> set hive.input.format;
hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat

每个map处理128M块最好?

不是的,假设一个块128M,但是字段比较少,所以数据量非常多,这时候也是可以拆分成好几个map去处理的!

reduce数量

1 reduce的数量可以自定义,map是没有直接自定义这个设置的
你说多少就是多少

hive> set mapred.reduce.tasks;
mapred.reduce.tasks=-1

2没有自定义的话会有默认值
主要依据下面这两个参数

//每个reducer任务处理的数据量,本里中是256M
hive> set hive.exec.reducers.bytes.per.reducer;
hive.exec.reducers.bytes.per.reducer=256000000
//每个任务的最大reducer数量,这里是1009
hive> set hive.exec.reducers.max ;
hive.exec.reducers.max=1009

本例中
reducer数量=min(1009,总输入数据量/256M)
举个例子

select name,count(1) from emp where dt='2020-9-1' group by name;

如果map端输出的总大小是9G,那么reducer数量就是9000/256=35
要减少reducer个数,可以调大hive.exec.reducers.bytes.per.reducer=256000000 比如调大到500M,reducer数目就是18个

reducer数目不是越多越好

同map一样,启动reducer也要消耗资源和时间,另外,有多少reduce,就会有多少小文件生成,如果这些小文件作为下一个任务的输入,就会造成小文件过多. namenode的负担也会加重

只有一个reducer的情况设置

有些情况设置了参数也没用,就是只有一个task
1,文件大小小于hive.exec.reducers.bytes.per.reducer,这个容易理解
2用了orderby,全局排序,只能一个reduce完成
3有笛卡尔乘积
4没有group by的汇总

map数量和reducer数量有关系么?

有一点关系,因为reducer的数量是由map输出的数据的大小决定的.map输出的数据量越大,reduce的数量相应的也就越多.

针对小文件过多如何优化?

  • map端开启小文件合并机制
  • 减少map的数量,map输出的文件是要落地的
  • 减少reduce的数量,因为reduce文件要落地的. 可以调大单个reduce处理的数据量

hive insert into比load数据慢的原因?

  • 类型检查
  • 多个mapreduce任务

为什么建议少用动态分区?

因为会产生大量小文件,比如一个表,不分区,就是一个文件. 按一个字段,分5个区,就是5个文件, 要是二级分区,比如依据省份分30个,依据城市分100个. 则会生成30*100=3000个小文件.用mr处理的话,就是3000个map task!

为什么少用动态分区,用时记得按distribute by分区?