MRCount - huangyuefeng/study GitHub Wiki

控制Map或Reduce的数量

src_url

有童鞋会问,怎么让hiveQL执行不占用机器所有资源?相信从他的感觉来看,执行复杂点的HQL,其他在运行的程序会出现明显的卡顿。

回顾下环境,是在PC上搭虚拟机,在虚拟机上搭建CentOS系统,在其上跑Hadoop和Hive。

这套环境是硬件有限的选择,一层包一层的,成本是较高点。

首先要看PC的硬件,

硬件上取决于PC机的CPU-CORE数量和内存大小,如果PC之后1个2个CORE,要跑复杂点的HQL,PC运行慢,也是可以理解的。

好在现在主流的PC都是4个CORE以上了,同时内存也会成为问题,

好在,Hive的习题册里,涉及数据量都比较小,起码数据量本身不会带来内存的压力,内存主要是被一层包一层的软件给消耗掉了。

那么,有什么办法可以想?

请去查看HQL的执行日志,hiveserver.log和hive.log。

cat *.log | grep -E "Hadoop job information|HDFS Read"

INFO  : Hadoop job information for Stage-1: number of mappers: 31; number of reducers: 1
INFO  : Stage-Stage-1: Map: 31  Reduce: 1   Cumulative CPU: 149.76 sec   HDFS Read: 4916467022 HDFS Write: 9 SUCCESS

类似如上应答,说明为了完成某个Hive任务的Stage-1,用了31个M和1个R。(M和R是缩写下同) 31个M做了4916467022B的HDFS Read,平均每个M分担了151MB的数据。

这里我们想到,每个M都是一个JVM进程,控制M的数量,就可以让HQL跑起来,不要给PC太大的压力。

当然,副作用是HQL会比原先跑得慢。

  • 先说结论

由于mapreduce中没有办法直接控制map数量,所以只能曲线救国,通过设置每个map中处理的数据量进行设置;reduce是可以直接设置的。

控制map和reduce的参数如下,我们先重点看控制map的3个。

set mapred.max.split.size=256000000;        -- 决定每个map处理的最大的文件大小,单位为B
set mapred.min.split.size.per.node=1;         -- 节点中可以处理的最小的文件大小
set mapred.min.split.size.per.rack=1;         -- 机架中可以处理的最小的文件大小
方法1

set mapred.reduce.tasks=10;  -- 设置reduce的数量

方法2

set hive.exec.reducers.bytes.per.reducer=1073741824 -- 每个reduce处理的数据量,默认1GB
  • 控制map数量的三个参数的逻辑概念

可以简单的理解为集群对一个表分区下面的文件进行分发到各个节点,之后根据mapred.max.split.size确认要启动多少个map数,逻辑如下 a.假设有两个文件大小分别为(256M,280M)被分配到节点A,那么会启动两个map,剩余的文件大小为10MB和35MB因为每个大小都不足241MB会先做保留 b.根据参数set mapred.min.split.size.per.node看剩余的大小情况并进行合并,如果值为1,表示a中每个剩余文件都会自己起一个map,这里会起两个,如果设置为大于4510241024则会合并成一个块,并产生一个map。 如果mapred.min.split.size.per.node为1010241024,那么在这个节点上一共会有4个map,处理的大小为(245MB,245MB,10MB,10MB,10MB,10MB),余下9MB。 如果mapred.min.split.size.per.node为4510241024,那么会有三个map,处理的大小为(245MB,245MB,45MB) 实际中mapred.min.split.size.per.node无法准确地设置成4510241024,会有剩余并保留带下一步进行判断处理 c. 对b中余出来的文件与其它节点余出来的文件根据mapred.min.split.size.per.rack大小进行判断是否合并,对再次余出来的文件独自产生一个map处理。

  • 控制map数量的简单实用方式

我们执行一个hive语句,发现起了31个map,我们认为这是不必要的,同时还会影响其它用户提交任务   这个时候希望map减小到25%左右,很简单,   将map处理的最大的文件大小增大,256000000*4=1024000000 参数修改为如下

set mapred.max.split.size=1024000000;
set mapred.min.split.size.per.node=1024000000;
set mapred.min.split.size.per.rack=1024000000;

再执行原先的hive语句,

cat *.log | grep -E "Hadoop job information|HDFS Read"

INFO  : Hadoop job information for Stage-1: number of mappers: 5; number of reducers: 1

INFO  : Stage-Stage-1: Map: 5  Reduce: 1   Cumulative CPU: 56.21 sec   HDFS Read: 4916360396 HDFS Write: 9 SUCCESS

这里我们看到,HDFS Read: 4916360396B,分担到5个M里,平均每个M是938M。 接近设置的1024MB。

至于为什么单个map处理的数据量增大了,map数却不是按倍数减少到1185/4=296个,原因在文件合并过程中对于如何起map数并不是单纯的看总的文件大小,存在一些更优化的算法,如考虑带宽、IO、当前map分配等。

  • 控制reduce参数

修改reduce的个数就简单很多,直接根据可能的情况作个简单的判断确认需要的reduce数量,如果无法判断,根据当前map数减小10倍,保持在1~100个reduce即可(注意,这个不同的集群需求不同哈) 设置参数如下

set mapred.reduce.tasks=10

不建议随意设置reduce参数哈,可能调整参数更好一点

set hive.exec.reducers.bytes.per.reducer=1073741824

  • 控制map和rduce数的说明

1、因为set参数的设置是session级别的,Toad for Cloud(青蛙)第三方软件中暂时没有发现如何使set的参数设置有效的,所以请使用CRT等工具连接到linux的界面中进行使用,使用hive命令连接hive集群,一次设置只要不结束这次连接,那么参数是始终有效的。

2、注意各参数的设置大小,不要冲突,否则会异常,大小顺序如下 mapred.max.split.size <= mapred.min.split.size.per.node <= mapred.min.split.size.per.rack

3、这种减少map数的行为是否能带来更短的执行时间,需要具体分析,map数也不是越少越好,减少了map数,单个map处理的数据量就上升,需要更多的时间,同时也因为需要合并节点内、节点间甚至机架之间的数据需要更多的IO和带宽。   参数的设置本质是根据文件情况、系统情况、数据计算情况进行的一个平衡考虑,有取有舍,我们需要遵循的规则就是:使大数据量利用合适的map数;使单个map任务处理合适的数据量

4、reduce数同3