【十八掌●武功篇】第十掌:根据一个错误探究MapJoin

一、出现的问题

在执行一个类似以下Hive SQL的时候,遇到一个报错,语句和报错信息如下:

select 
 h.ID_1
,h.ID_2
,h.ID_3
,h.ID_4
,h.ID_5
,h.ID_6
,h.ID_7
,h.ID_8
,h.ID_9
,h.change_code
,h.s_date
,'2018-05-07' as e_date
from (
    select 
     ID_1
    ,ID_2
    ,ID_3
    ,ID_4
    ,ID_5
    ,ID_6
    ,ID_7
    ,ID_8
    ,ID_9
    ,change_code
    ,s_date
     from test_his where dt='2018-05-06'
 )  h
left join
(
   select 
    ID_1
   ,ID_2
   ,ID_3
   ,ID_4
   ,ID_5
   ,ID_6
   ,ID_7
   ,ID_8
   ,ID_9
   from s_test where dt='2018-05-07'
) s
on  h.id_1=s.id_1
where  
h.change_code='1';

报错如下:

Query ID = dw_20180519141227_8574d58f-01db-4ecc-a1f7-ad5d9d0135eb
Total jobs = 1
Execution log at: /tmp/dw/dw_20180519141227_8574d58f-01db-4ecc-a1f7-ad5d9d0135eb.log
2018-05-19 14:12:31 Starting to launch **local task to process map join**;  maximum memory = 1046478848
2018-05-19 14:12:33 Processing rows:    200000  Hashtable size: 199999  Memory usage:   42522416    percentage: 0.041
2018-05-19 14:12:33 Processing rows:    300000  Hashtable size: 299999  Memory usage:   53507040    percentage: 0.051
2018-05-19 14:12:33 Processing rows:    400000  Hashtable size: 399999  Memory usage:   64643144    percentage: 0.062
...
...
...
2018-05-19 14:12:47 Processing rows:    6800000 Hashtable size: 6799999 Memory usage:   929123264   percentage: 0.888
2018-05-19 14:12:47 Processing rows:    6900000 Hashtable size: 6899999 Memory usage:   948926952   percentage: 0.907
Execution failed with exit status: 3
Obtaining error information

Task failed!
Task ID:
  Stage-4

Logs:

/tmp/dw/hive.log
FAILED: Execution Error, return code 3 from org.apache.hadoop.hive.ql.exec.mr.MapredLocalTask

二、分析错误原因

这个语句大体是test_his 表和s_test表进行left join,其中test_his表数据量很小,只有几行数据,s_test表有对应分区文件大约200M。

第一个问题是:看日志可以看出,是执行的MapJoin,但是应该是当只有参与关联的数据量比较小时才启用MapJoin,这个语句中,右表数据200M,是比较大的,怎么启用了MapJoin了呢?

第二个问题是:看日志“Starting to launch local task to process”,为什么是local task呢?难道是hive 语句没有到Yarn上去执行MapReduce任务还是在本地模式下执行的?

1、先找第一个问题的原因,为什么启用MapJoin了呢?

查询参数set hive.auto.convert.join,结果为true,意味着hive会根据输入数据文件的大小判断是否将普通的reduce join转换为mapjoin。

查询参set hive.mapjoin.smalltable.filesize,结果为25000000,意味着参与join的小表的数据量小于25M就会启用mapjoin,但是当前这个例子中,右表的数据量是200M,明显大于25M,怎么就启用了mapjoin了呢?接着向下找原因。

还有一个和mapjoin相关的参数,set hive.auto.convert.join.noconditionaltask,结果为true,那么意思就是:hive会根据join方的表的大小判断是否启用mapjoin,如果有N个表进行join,hive会判断其中n-1个表参与join的数据文件大小总和是否小于hive.auto.convert.join.noconditionaltask.size的值,如果小于这个值,就把这个N-1个的数据放入map的内存里,进行mapjoin。

再查询参数hive.auto.convert.join.noconditionaltask.size的值,结果为1431655765,是1.33G(不要问我为什么设置的这么大,我也不知道),这个参数的默认值是10M,这个参数怎么这么大? 那么意思就是:N-1个表的总和如果小于1.3G就启动mapjoin。到这里就知道了,s_test表的数据是200M,启动mapjoin就正常了。

2、第一个问题怎么解决呢?

修改hive.auto.convert.join.noconditionaltask.size参数的值,改小这个值,比如改为默认的10M,就不会启动MapJoin了。另外设置hive.auto.convert.join为false也可以停用mapjoin,但是这种方式不好,它会停用所有的mapjoin。

3、第二个问题,为什么有Local task呢?

难道是这个hive sql 不会到yarn上去执行,而是用本地模式执行?那么查一下官网文档mapjoin执行的详细步骤:

(1) 再启动mapreduce之前,先启动一个local task,这个task是从HDFS上读取数据变为hashtable的形式到内存中

(2) 将hashtable数据进行序列化存入硬盘,并且对数据进行压缩为一个tar文件。

(3) 然后将这个tar文件传递到分布式缓存Hadoop Distributed Cache中。

(4) 将这个tar文件分发到各个mapper中,存入硬盘并进行解压,读取到内存中。

(5) 各个map中读取这个hashtable进行join操作。

通过这个过程可以看到,出错的hive sql运行日志中显示的local task是mapjoin第一步中产生的,这个local task是用来读取文件并生成hashtable,读取200M的orc数据并展开读取本地内存中,会有非常大的数据量,导致内存溢出也就不足为奇了。

三、解决问题

总而言之,这个问题的根本原因在于hive.auto.convert.join.noconditionaltask.size参数设置的过大,减小这个值就可以了。

发布了74 篇原创文章 · 获赞 74 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/chybin500/article/details/80389932