在使用sqoop的过程中,我们常常会因为资源不足或者传输的数据量太大而导致 GC overhead limit exceeded和 Halting due to Out Of Memory Error 错误,那么遇到这种情况,我们可以怎么解决呢?
出现这种类型的错误,我们可以考虑从以下两种情况进行排查:
情况一:
参数:--split-by `field`,用来切割的field字段,分布不均匀,导致了抽数过程中的数据倾斜。一般情况下,参数:--split-by `field`与参数:-m N是一起使用的,-m N使用来指定Map Task的数量的。--split-by切割的思想如下:
- 先查出--split-by `field`中field的最小值和最大值(一般所指定的field为数字类型的值)。
- 然后根据Map Task的个数对(max(field)-min(field))之间的数据进行均匀的范围切分。
这样就会导致数据倾斜的情况出现:
假设field的最小值为1,最大值为20,Map Task的个数为4,理想的情况下,每个Map Task将会分到5条数据,但实际上,虽然最大值为20,但是中间field值为6-19的值是缺失的,那么分配到field值为1-5的Map Task实际上就承担了大部分的工作,这就会造成数据倾斜。或许所举例子的数据量看着比较少,但是如果是在数据量很大的情况下呢?
- Sqoop数据倾斜解决方案
出现数据倾斜的情况主要是由于--split-by `field`中的field字段,在切割的时候分布不均匀,那是否只要保证切割的字段能让数据均匀地分布到每个Map Task中就能解决数据倾斜的问题呢?
根据这个思想,我们可以在参数:--query “select a,b,c from table”,给每一行数据排序,并用排序后的字段`rank` 进行切割,如:
--query “select id,name,city
from
(select row_number() as rank,id,name,age,city
from mysql_database.table_name)a where $CONDITIONS” \
--boundary-query “select 1 as min,count(1) as max from mysql_database.table_name” \
要注意的是,由于排序后的`rank`字段在原表中是不存在的,因此我们需要加上下面的参数,否则会报invalid identifier的错误:
--boundary-query “select 1 as min,count(1) as max from mysql_database.table_name” \
情况二:
数据传输过程中,sqoop程序在yarn中申请到内存资源不足,导致了内存溢出。
- 内存资源不足解决方案
-
使用参数:-D <key=value>,增加每个Map Task的资源。
-D yarn.scheduler.minimum-allocation-mb=8096
-D yarn.scheduler.maximum-allocation-mb=16192
-D mapreduce.map.memory.mb=8096
-D mapreduce.reduce.memory.mb=8096 \
参数解析:
(1) yarn.scheduler.minimum-allocation-mb
每个container向RM申请内存的最小大小(单位:MB)。申请的内存小于此值,那么实际申请到内存也是该值的大小。
(2) yarn.scheduler.maximum-allocation-mb
每个container向RM申请内存的最大大小(单位:MB)。申请的内存大于此值,那么实际申请到内存的最多只能是该值的大小。需要注意的是,该参数设置的值一定要小于集群自身配置的最大值,否则会报错。
(3) mapreduce.map.memory.mb
一个 Map Task 可使用的内存上限(单位:MB),默认为 1024 MB。如果 Map Task 实际使用的资源量超过该值,则会被强制杀死。
(4) n mapreduce.reduce.memory.mb
一个 Reduce Task 可使用的内存上限(单位:MB),默认为 1024 MB。如果 Reduce Task 实际使用的资源量超过该值,则会被强制杀死。
2. 使用参数:-m N,增加Map Task的数量。
--split-by `field` \
-m 10 \
参数解析:
参数:-m N,等同于–num-mappers N,可以用来指定Map Task的数量,默认数量是4个。当指定N为1的时候,只有1个Map Task,可以不用设置split-by参数;当我们不使用该参数,或指定的N大于1的时候,需要指定参数:--split-by `fied`。
以上的解决方案,可以单独使用,也可以混合一起使用,以上完整的调参如下:
sqoop import \
-D mapreduce.job.queuename=job_xxxxx \
-D yarn.scheduler.minimum-allocation-mb=8096 \
-D yarn.scheduler.maximum-allocation-mb=16192 \
-D mapreduce.map.memory.mb=8096 \
-D mapreduce.reduce.memory.mb=8096 \
--connect jdbc:mysql://localhost:3306/your_database \
--username root \
--password 123456 \
--query “select id,name,city
from
(select row_number() as rank,id,name,age,city
from mysql_database.table_name)a where $CONDITIONS” \
--boundary-query “select 1 as min,count(1) as max from mysql_database.table_name” \
--delete-target-dir \
--target-dir /user/hive/temp/xxxxx \
--split-by rank \
--null-string '\\N' \
--null-non-string '\\N' \
--fields-terminated-by '\0x1E' \
--hive-import \
--hive-overwrite \
--hive-drop-import-delims \
--hive-database hive_database \
--hive-table hive_table_Name \
--fetch-size 1000 \
-m 10 \
--mapreduce-job-name xxxxx