map 数和reduce数
控制hive任务中的map数
合适的map数,会让资源分配的更平均,让我们的代码运行更快,通常情况下,作业会通过input的目录产生一个或者多个map任务。我们可以通过调整参数来控制运行过程中的map数。
Hive Map的数量主要取决于以下几个因素:
输入文件的个数:Hive作业会根据输入目录产生的map任务数量来决定,这通常与输入文件的个数有关。
输入文件的总大小:如果单个输入文件非常大,或者整个作业的任务逻辑比较复杂,导致Map阶段执行缓慢时,可以增加Map数以减少每个Map处理的文件数据量。
集群设置的文件块大小:在Hive中可以通过`set dfs.block.size`命令查看并设置集群的默认文件块大小。这个参数通常是固定的,不能被用户自定义修改。
小文件进行合并,减少 map 数
如果小文件多,
在map输入时,一个小文件产生一个map任务,这样会产生多个map任务;启动和初始化多个map会消耗时间和资源,所以hive默认是将小文件合并成大文件。
在map执行前合并小文件,减少map数:CombineHiveInputFormat具有对小文件进行合并的功能(系统默认的格式)。HiveInputFormat没有对小文件合并功能。
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| -------小文件合并
set hive.input.format= org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
-------关闭小文件合并大文件(不推荐调整)
set hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat;
|
案例实操:该表内2个分区,共5个文件
|-----------------------------------------------------------------------------------------------------------|
| hive (``default``)>
select
name
,count(*) as cnt
from ds_hive.ch4_t_par_emp
group by name
;
|
默认情况下执行(开启小文件合并),按分区执行,2个map

关闭小文件合并后执行,按文件个数执行,5个map

注:时间虽然贴的是一个20s,一个18s,貌似有点优化,但实际有波动,应该在增大数据量,忽略波动时间的影响才可能看出优化效果。
在Map-Reduce的任务结束时合并小文件的设置:如果map输出的小文件过多,hive 默认是开启map 输出合并。
|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 在map-only任务结束时合并小文件,默认``true
set hive.merge.mapfiles = ``true``;
在map-reduce任务结束时合并小文件,默认``false
set hive.merge.mapredfiles = ``true``;
合并文件的大小,默认256M
set hive.merge.size.per.task = ``268435456``;
当输出文件的平均大小小于该值时,启动一个独立的map-reduce任务进行文件merge
set hive.merge.smallfiles.avgsize = ``16777216``;
-- 设置最大(小)切片值 set mapreduce.input.fileinputformat.split.maxsize=256000000;
set mapreduce.input.fileinputformat.split.minsize=1;
|
复杂文件增加 Map 数
当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。
增加map的方法为:根据
computeSliteSize(Math.max(minSize,Math.min(maxSize,blocksize)))=blocksize=128M
max(最小切片值,max(最大切片值,默认块大小))
公式,调整maxSize最大值。让maxSize最大值低于blocksize就可以增加map的个数。

设置最大切片值为200个字节
|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| hive (``default``)>
set mapreduce.input.fileinputformat.split.maxsize=``200``;
select
name
,count(*) as cnt
from ds_hive.ch4_t_par_emp
group by name
;
|
相较于默认情况,map数由2个增大到3个

控制hive任务中的reduce数
reduce个数的设定极大影响任务执行效率,在设置reduce个数的时候需要考虑这两个原则:使大数据量利用合适的reduce数;使每个reduce任务处理合适的数据量。
在不指定reduce个数的情况下,Hive会猜测确定一个reduce个数,基于以下,两个设定:
参数1:hive.exec.reducers.bytes.per.reducer(每个reduce任务处理的数据量,在Hive 0.14.0及更高版本中默认为256M)
参数2:hive.exec.reducers.max(每个任务最大的reduce数,在Hive 0.14.0及更高版本中默认为1009)

计算reducer数的公式: N = min( 参数2,总输入数据量 / 参数1 )
在生产中,一般不调整这两个参数,这两个参数是 如果我们不指定hive的reduce个数,hive程序通过上面两个参数进行动态计算 决定reduce的个数。
mapred.reduce.tasks (默认是-1,代表hive自动根据输入数据设置reduce个数)
一般在生产中对reduce的个数也不做太多调整,但是有时候reduce的个数太多,hdfs上的小文件太多。 此时就可以通过 调小mapreduce.job.reduces的个数,来减少hdfs上输出文件的个数。
reduce个数并不是越多越好,启动和初始化reduce会消耗时间和资源;另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题。
job并行运行设置
带有子查询的hql,如果子查询间没有依赖关系,可以开启任务并行,设置任务并行最大线程数。
hive.exec.parallel (默认是false, true:开启并行运行)
hive.exec.parallel.thread.number (最多可以并行执行多少个作业, 默认是 8)

测试并行运算:
关闭并行运行,发现没有依赖的子查询不会同步执行
-- 关闭并行运行, 默认是false
关闭并行执行日志如下:

开启并行执行日志如下:

Fetch 抓取
Fetch抓取是指,Hive中对某些情况的查询可以不必使用MapReduce计算。例如:select * from emp;在这种情况下,Hive可以简单地读取emp对应的存储目录下的文件,然后输出查询结果到控制台。
相关参数如下:
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| --是否在特定场景转换为fetch 任务
--设置为none表示不转换
set hive.fetch.task.conversion=none;
--设置为minimal表示支持select *,分区字段过滤,Limit等
set hive.fetch.task.conversion=minimal;
--设置为more表示支持select 任意字段,包括函数,过滤,和limit等
set hive.fetch.task.conversion=more;
|

本地模式
大多数的Hadoop Job是需要Hadoop提供的完整的可扩展性来处理大数据集的。不过,有时Hive的输入数据量是非常小的。在这种情况下,为查询触发执行任务消耗的时间可能会比实际job的执行时间要多的多。对于大多数这种情况,Hive可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显被缩短。
相关参数如下:
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| --开启自动转换为本地模式
set hive.exec.mode.local.auto=``true``;
--设置local MapReduce的最大输入数据量,当输入数据量小于这个值时采用local MapReduce的方式,默认为``134217728``,即128M
set hive.exec.mode.local.auto.inputbytes.max=``50000000``;
--设置local MapReduce的最大输入文件个数,当输入文件个数小于这个值时采用local MapReduce的方式,默认为``4
set hive.exec.mode.local.auto.input.files.max=``10``;
|

严格模式
Hive可以通过设置某些参数防止危险操作:
1 )分区表不使用分区过滤
将hive.strict.checks.no.partition.filter设置为true时,对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行。换句话说,就是用户不允许扫描所有分区。进行这个限制的原因是,通常分区表都拥有非常大的数据集,而且数据增加迅速。没有进行分区限制的查询可能会消耗令人不可接受的巨大资源来处理这个表。
2 )使用order by 没有limit 过滤
将hive.strict.checks.orderby.no.limit设置为true时,对于使用了order by语句的查询,要求必须使用limit语句。因为order by为了执行排序过程会将所有的结果数据分发到同一个Reduce中进行处理,强制要求用户增加这个limit语句可以防止Reduce额外执行很长一段时间(开启了limit可以在数据进入到Reduce之前就减少一部分数据)。
3 )笛卡尔积
将hive.strict.checks.cartesian.product设置为true时,会限制笛卡尔积的查询。对关系型数据库非常了解的用户可能期望在执行JOIN查询的时候不使用ON语句而是使用where语句,这样关系数据库的执行优化器就可以高效地将WHERE语句转化成那个ON语句。不幸的是,Hive并不会执行这种优化,因此,如果表足够大,那么这个查询就会出现不可控的情况。
CBO 优化
CBO是指Cost based Optimizer,即基于计算成本的优化。
在Hive中,计算成本模型考虑到了:数据的行数、CPU、本地IO、HDFS IO、网络IO等方面。Hive会计算同一SQL语句的不同执行计划的计算成本,并选出成本最低的执行计划。目前CBO在hive的MR引擎下主要用于join的优化,例如多表join的join顺序。
相关参数为:
|-----------------------------------------------|
| --是否启用cbo优化
set hive.cbo.enable=``true``;
|
4 )总结
CBO优化对于执行计划中join顺序是有影响的,其之join顺序提前,如果某张表的数据量较小,将其提前,会有更大的概率使得中间结果的数据量变小,从而使整个计算任务的数据量减小,也就是使计算成本变小。