内存溢出
Client端内存溢出
Client端产生内存溢出执行上面的看是很简略的一条sql语句:
hive> select count(1) from test_tb_1_1;
Query ID = hdfs_20180802104347_615d0836-cf41-475d-9bec-c62a1f408b21Total jobs = 1Launching Job 1 out of 1Number of reduce tasks determined at compile time: 1In order to change the average load for a reducer (in bytes):set hive.exec.reducers.bytes.per.reducer=<number>In order to limit the maximum number of reducers:set hive.exec.reducers.max=<number>In order to set a constant number of reducers:set mapreduce.job.reduces=<number>FAILED: Execution Error, return code -101 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask. Java heap space
报错起因:
该语句会进行全表全分区扫描,如果该表的分区数很多,数据量很大,可能就会呈现客户端内存不足的报错。
注:客户端报内存溢出的判断根据,通过查看客户端输入来的日志中,还没有打印出作业的application id 信息(信息款式如下)就报内存溢出的异样了,在ResourceManager上也查看不到该作业的任何信息。
因为是客户端,在启动hive的时候就要指定好参数,启动之后批改不了,因而须要在启动hive命令之前,先批改环境变量
export HIVE_CLIENT_OPTS="-Xmx1536m -XX:MaxDirectMemorySize=512m"
默认值是 1.5G , 能够依据须要调大一些。 例如:
export HIVE_CLIENT_OPTS="-Xmx2g -XX:MaxDirectMemorySize=512m"
ApplicationMaster端内存溢出
为了演示,先将am的内存调小到 512M。 之后客户端输出如下的报错信息:
set yarn.app.mapreduce.am.resource.mb=512;
select count(1) from (select num, count(1) from default.test_tb_1_1 where part>180 and part<190 group by num) a;
Query ID = hdfs_20180802155500_744b90db-8a64-460f-a5cc-1428ae61931bTotal jobs = 2Launching Job 1 out of 2Number of reduce tasks not specified. Estimated from input data size: 849In order to change the average load for a reducer (in bytes):set hive.exec.reducers.bytes.per.reducer=<number>In order to limit the maximum number of reducers:set hive.exec.reducers.max=<number>In order to set a constant number of reducers:set mapreduce.job.reduces=<number>Starting Job = job_1533068988045_266336, Tracking URL = http://hadoop-jrsy-rm02.pekdc1.jdfin.local:8088/proxy/application_1533068988045_266336/Kill Command = /soft/home/hadoop/bin/hadoop job -kill job_1533068988045_266336Hadoop job information for Stage-1: number of mappers: 0; number of reducers: 02018-08-02 15:55:48,910 Stage-1 map = 0%, reduce = 0%Ended Job = job_1533068988045_266336 with errorsError during job, obtaining debugging information...FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTaskMapReduce Jobs Launched:Stage-Stage-1: HDFS Read: 0 HDFS Write: 0 FAILTotal MapReduce CPU Time Spent: 0 msec
从报错信息中能够看出有application id 信息,阐明工作提交到了ResourceManager上,不是client端导致的,然而有没有看到container容器失败的信息,间接0%后就失败,初步能够判断是因为application master内存不足导致的,
进一步确定须要两种路径都行:
1)在ResourceManager的界面查看程序的运行信息,能够看到如下的报错信息:
Current usage: 348.2 MB of 512 MB physical memory used; 5.2 GB of 1.5 GB virtual memory used. Killing container.
阐明内存溢出了,Application application_1533068988045_266336 failed 2 times due to AM Container for appattempt_1533068988045_266336_000002 exited with exitCode: -103
能够确定是application master产生的内存溢出。
2)能够应用yarn的命令来获取日志信息
yarn logs -applicationId application_1533068988045_266336 > application_1533068988045_266336.log
该日志信息中的数据和下面的一样,判断形式雷同。注:应用该命令要留神日志文件很大的作业,因而最好不在系统盘做这件事。
解决办法:
application master 内存溢出设置上面的参数(单位MB)
set yarn.app.mapreduce.am.resource.mb=4096;
set yarn.app.mapreduce.am.command-opts=-Xmx3276M;注:java.opts是memory.mb的80%左右
set yarn.app.mapreduce.am.resource.cpu-vcores=8; --MR ApplicationMaster占用的虚构CPU个数
Map端 和 Reduce端内存溢出
Map端 和 Reduce端呈现内存溢出的标记:
首先会看到提醒container内存不足的信息(例如:beyond physical memory):
Container [pid=192742,containerID=container_e1383_1533068988045_250824_01_003156] is running beyond physical memory limits. Current usage: 2.0 GB of 2 GB physical memory used; 3.7 GB of 6.2 GB virtual memory used. Killing container.
通过日志信息中attempt信息 attempt_1533068988045_250824_r_000894_3 两头的 r 和 m 能够看出是map还是reduce容器挂了。
此处的谬误日志是reduce容器挂了。
Task with the most failures(4):Task ID:task_1533068988045_250824_r_000894URL:http://0.0.0.0:8088/taskdetails.jsp?jobid=job_1533068988045_250824&tipid=task_1533068988045_250824_r_000894Diagnostic Messages for this Task:Container [pid=192742,containerID=container_e1383_1533068988045_250824_01_003156] is running beyond physical memory limits. Current usage: 2.0 GB of 2 GB physical memory used; 3.7 GB of 6.2 GB virtual memory used. Killing container.
解决形式:在执行sql语句前 批改如下的参数(单位MB):
Map容器内存溢出设置上面的参数
set mapreduce.map.memory.mb=4096;
set mapreduce.map.java.opts=-Xmx3276M;
Reduce容器内存溢出设置上面的参数
set mapreduce.reduce.memory.mb=4096;
set mapreduce.reduce.java.opts=-Xmx3276M;注:java.opts是memory.mb的80%左右依据内存溢出时提醒的内存大小(例如:Current usage: 2.0 GB of 2 GB physical memory used)进行增大内存,增大的最小单位是1GB。
参数介绍:
mapreduce.map.memory.mb: 一个Map Task可应用的内存下限(单位:MB),默认为 1024。如果 Map Task理论应用的资源量超过该值,则会被强制杀死;
mapreduce.map.java.opts: Map Task 的 JVM 参数,你能够在此配置默认的java heap size参数,个别设置成上一个参数的70%-80%.
mapreduce.reduce.memory.mb: 一个 Reduce Task 可应用的资源下限(单位:MB),默认为1024。如果Reduce Task理论应用的资源量超过该值,则会被强制杀死;
mapreduce.reduce.java.opts: Reduce Task 的 JVM 参数,你能够在此配置默认的java heap size参数,个别设置成上一个参数的70%-80%.
mapreduce.map.cpu.vcores
mapreduce.reduce.cpu.vcores
数据歪斜
景象
简略来说数据歪斜就是数据的key 的分化重大不均,造成一部分数据很多,一部分数据很少的场面。
举个 word count 的入门例子,它的map 阶段就是造成 (“aaa”,1)的模式,而后在reduce 阶段进行 value 相加,得出 “aaa” 呈现的次数。若进行 word count 的文本有100G,其中 80G 全副是 “aaa” 剩下 20G 是其余单词,那就会造成 80G 的数据量交给一个 reduce 进行相加,其余 20G 依据 key 不同扩散到不同 reduce 进行相加的状况。如此就造成了数据歪斜,临床反馈就是 reduce 跑到 99%而后始终在原地等着 那80G 的reduce 跑完。
常见容易呈现数据歪斜的操作
join
参数:
- mapjoin
set hive.auto.convert.join=true; 依据输出文件的大小决定是否将一般join转换为mapjoin的一种优化,默认不开启false;
set hive.mapjoin.smalltable.filesize=50000000; # The threshold for the input file size of the small tables; if the file size is smaller than # this threshold, it will try to convert the common join into map join
- Skew Join
set hive.optimize.skewjoin=true;--如果是join 过程呈现歪斜 应该设置为true
set hive.skewjoin.key=100000; --这个是join的键对应的记录条数超过这个值则会进行分拆,值依据具体数据量设置。 hive在运行的时候没有方法判断哪个key会产生多大的歪斜,所以应用这个参数管制歪斜的阈值,如果超过这个值,新的值会发送给那些还没有达到的reduce, 个别能够设置成(解决的总记录数/reduce个数)的2-4倍都能够承受.
场景:
1》大表和小表关联时
比方,一个上千万行的记录表和一个几千行表之间join关联时,容易产生数据歪斜。为什么大表和小表容易产生数据歪斜(无非有的reduce执行工夫被提早)?可参考Hive中小表与大表关联(join)的性能剖析
解决形式:
1) 多表关联时,将小表(关联键记录少的表)顺次放到后面,这样能够触发reduce端更少的操作次数,缩小运行工夫。
2) 同时能够应用Map Join让小的维度表缓存到内存。在map端实现join过程,从而省略掉reduce端的工作。然而应用这个性能,须要开启map-side join的设置属性:set hive.auto.convert.join=true(默认是false)。同时还能够设置应用这个优化的小表的大小:set hive.mapjoin.smalltable.filesize=25000000(默认值25M)
3) Mapjoin,在map阶段实现join,不须要reduce.
Select /*+ MAPJOIN(b) */ a.key, a.value from a join b on a.key = b.key
2》大表和大表的关联
1)比方:大表和大表关联,然而其中一张表的多是空值或者0比拟多,容易shuffle给一个reduce,造成运行慢。
解决办法:把空置变成一个字符串加上随机数,把歪斜的数据分布到不同的reduce上
select * from log aleft outer join users bon case when a.user_id is null then concat(‘hive’,rand() ) else a.user_id end = b.user_id;
3》Join 关联,不同数据类型关联产生数据歪斜,易呈现数据歪斜
场景:用户表中user_id字段为int,log表中user_id字段既有string类型也有int类型。当依照user_id进行两个表的Join操作时,默认的Hash操作会按int型的id来进行调配,这样会导致所有string类型id的记录都调配到一个Reducer中。
能够思考把数据类型转化为同一数据类型
select * from user a left outer join log bon a.user_id=cast(b.user_id as string);
4》join防止多对多关联
在join链接查问时,确认是否存在多对多的关联,起码保障有一个表的后果集的关联字段不反复
5》join防止笛卡尔乘积
在join链接查问中没有on链接,而通过where条件语句会产生笛卡尔集
问题: Select a.key, b.value from A a join B b where a.key=b.key
优化:Select a.key, b.value from A a join B b on a.key=b.key
groupby
group by 维度过小,某值的数量过多
set hive.map.aggr=true;
set hive.groupby.skewindata=true;
hive.map.aggr=true:
在map中会做局部汇集操作,效率更高但须要更多的内存。
hive.groupby.skewindata=true:
数据歪斜时负载平衡,当选项设定为true,生成的查问打算会有两个MRJob。
第一个MRJob 中,Map的输入后果汇合会随机散布到Reduce中,每个Reduce做局部聚合操作,并输入后果,这样解决的后果是雷同的GroupBy Key有可能被散发到不同的Reduce中,从而达到负载平衡的目标;第二个MRJob再依据预处理的数据后果依照GroupBy Key散布到Reduce中(这个过程能够保障雷同的GroupBy Key被散布到同一个Reduce中),最初实现最终的聚合操作。
count distinct
如果数据量十分大,执行如select a,count(distinct b) from t group by a;类型的SQL时,会呈现数据歪斜的问题。
解决形式:应用sum...group by代替。如select a,sum(1) from (select a,b from t group by a,b) group by a;
尽量避免应用distinct 进行排重,特地是大表操作,用group by 代替
优化前:Select count (distinct key) from a
优化后:Select sum(1) from (Select key from a group by key) t
调整map/reduce个数
管制hive工作中的map数
- 通常状况下,作业会通过input的目录产生一个或者多个map工作。
次要的决定因素有: input的文件总个数,input的文件大小,集群设置的文件块大小(目前为128M, 可在hive中通过set dfs.block.size;命令查看到,该参数不能自定义批改);
- 举例:
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数越多越好?
答案是否定的。如果一个工作有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map工作来实现,而一个map工作启动和初始化的工夫远远大于逻辑解决的工夫,就会造成很大的资源节约。而且,同时可执行的map数是受限的。
- 是不是保障每个map解决靠近128m的文件块,就居安思危了?
答案也是不肯定。比方有一个127m的文件,失常会用一个map去实现,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map解决的逻辑比较复杂,用一个map工作去做,必定也比拟耗时。
针对下面的问题3和4,咱们须要采取两种形式来解决:即缩小map数和减少map数;
如何合并小文件,缩小map数?
假如一个SQL工作:
Select count(1) from popt_tbaccountcopy_mes where pt = ‘2012-07-04’; 该工作的inputdir /group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04 共有194个文件,其中很多是远远小于128m的小文件,总大小9G,失常执行会用194个map工作。 Map总共耗费的计算资源: SLOTS_MILLIS_MAPS= 623,020 我通过以下办法来在map执行前合并小文件,缩小map数:
set hive.hadoop.supports.splittable.combineinputformat = true; set mapred.max.split.size=100000000;---每个Mapper工作解决的数据量的最大值. (这个值决定了合并后文件的大小) set mapred.min.split.size.per.node=100000000;---一个节点上split片的最小值(这个值决定了多个DataNode上的文件是否须要合并) set mapred.min.split.size.per.rack=100000000;---一个交换机下split片的最小值(这个值决定了多个交换机上的文件是否须要合并) set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
再执行下面的语句,用了74个map工作,map耗费的计算资源:SLOTS_MILLIS_MAPS= 333,500
对于这个简略SQL工作,执行工夫上可能差不多,但节俭了一半的计算资源。
大略解释一下,100000000示意100M, set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;这个参数示意执行前进行小文件合并,
后面三个参数确定合并文件块的大小,大于文件块大小128m的,依照128m来分隔,小于128m,大于100m的,依照100m来分隔,把那些小于100m的(包含小文件和分隔大文件剩下的),
进行合并,最终生成了74个块。
如何适当的减少map数?
当input的文件都很大,工作逻辑简单,map执行十分慢的时候,能够思考减少Map数,来使得每个map解决的数据量缩小,从而进步工作的执行效率。
假如有这样一个工作:
Select data_desc, count(1), count(distinct id), sum(case when …), sum(case when ...), sum(…)from a group by data_desc
如果表a只有一个文件,大小为120M,但蕴含几千万的记录,如果用1个map去实现这个工作,必定是比拟耗时的,这种状况下,咱们要思考将这一个文件正当的拆分成多个,
这样就能够用多个map工作去实现。
set mapred.reduce.tasks=10;create table a_1 as select * from a distribute by rand(123);
这样会将a表的记录,随机的扩散到蕴含10个文件的a_1表中,再用a_1代替下面sql中的a表,则会用10个map工作去实现。
每个map工作解决大于12M(几百万记录)的数据,效率必定会好很多。
看上去,貌似这两种有些矛盾,一个是要合并小文件,一个是要把大文件拆成小文件,这点正是重点须要关注的中央,
依据理论状况,管制map数量须要遵循两个准则:使大数据量利用适合的map数;使单个map工作解决适合的数据量;
管制hive工作的reduce数
1. Hive本人如何确定reduce数:
reduce个数的设定极大影响工作执行效率,不指定reduce个数的状况下,Hive会猜想确定一个reduce个数,基于以下两个设定:
hive.exec.reducers.bytes.per.reducer(每个reduce工作解决的数据量,默认为1000^3=1G)
hive.exec.reducers.max(每个工作最大的reduce数,默认为1009)
计算reducer数的公式很简略N=min(参数2,总输出数据量/参数1) 即min( hive.exec.reducers.max ,总输出数据量/hive.exec.reducers.bytes.per.reducer )
即,如果reduce的输出(map的输入)总大小不超过1G,那么只会有一个reduce工作;
如:select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;
/group/p_sdo_data/p_sdo_data_etl/pt/popt_tbaccountcopy_mes/pt=2012-07-04 总大小为9G多,因而这句有10个reduce
reduce个数并不是越多越好;
同map一样,启动和初始化reduce也会耗费工夫和资源;
另外,有多少个reduce,就会有多少个输入文件,如果生成了很多个小文件,那么如果这些小文件作为下一个工作的输出,则也会呈现小文件过多的问题;
2. 调整reduce个数:
办法一: 调整hive.exec.reducers.bytes.per.reducer参数的值;
set hive.exec.reducers.bytes.per.reducer=500000000; (500M)
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt; 这次有20个reduce
办法二: 调整reduce个数mapred.reduce.tasks;
set mapred.reduce.tasks = 15;
select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;这次有15个reduce
3. 什么状况下只有一个reduce;
很多时候你会发现工作中不论数据量多大,不论你有没有设置调整reduce个数的参数,工作中始终都只有一个reduce工作;
其实只有一个reduce工作的状况,除了数据量小于hive.exec.reducers.bytes.per.reducer参数值的状况外,还有以下起因:
a) 没有group by的汇总
比方把select pt,count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04' group by pt;
写成 select count(1) from popt_tbaccountcopy_mes where pt = '2012-07-04';
这点十分常见,心愿大家尽量改写。
b) 用了Order by
c) 有笛卡尔积
通常这些状况下,除了找方法来变通和防止,我临时没有什么好的方法,因为这些操作都是全局的,所以hadoop不得不用一个reduce去实现;
同样的,在设置reduce个数的时候也须要思考这两个准则:使大数据量利用适合的reduce数;使单个reduce工作解决适合的数据量;
主动合并输入的小文件
set hive.merge.mapfiles = true;
set hive.merge.mapredfiles = true;
set hive.merge.size.per.task = 256000000;
set hive.merge.smallfiles.avgsize=256000000;
set hive.merge.orcfile.stripe.level=false;
hive tez insert union all问题
(1)问题形容
在hive中应用tez模式时,发现tez的输入后果在对应表目录中,生成了子目录,造成未配置tez的hive客户端对该表进行读取时,无奈获取到数据。
检查表的输入目录,在分区目录下发现了两个子目录:1和2:
/user/hive/test1/20170920000000/1
/user/hive/test1/20170920000000/2
(2)起因
查看对应的sql,发现存在insert union操作,查看往上信息,发现tez对于insert union操作会进行优化,通过并行加快速度,为避免有雷同文件输入,所以对并行的输入各自生成成了一个子目录,在子目录中寄存后果。如果此时全副hive客户端引擎及相干都设定为tez,则无问题。如果有客户端还在应用mr引擎,则会呈现读取不到数据的状况。而在hive中,对于表目录寄存信息有如下策略:
在Hive默认设置中,hive只能读取对应表目录下的文件,
1)如果表目录中同时存在目录和文件
则应用hive进行读取时,会报目录非文件的谬误
2)表目录中只有目录
hive不会进行深层递归查问,只会读取到对应查问目录,会查问后果为空
3)表目录中只有文件
则能够失常查问
Hive on tez 中对insert union操作进行了优化,会并行输入到对应的表目录,为避免有雷同名文件存在,所以为各自输入在表目录下各自设置了一个目录,里边寄存执行后果
此种目录,对tez引擎客户端可读。然而对于mr引擎,因为其只会便遍历到对应分区层。发现分区下没有文件后,就返回空
(3)解决思路
发现该景象后,从网上搜寻解决方案,http://grokbase.com/t/hive/us...
经该文章提醒,能够开启mapreduce的递归查问模式:
set mapreduce.input.fileinputformat.input.dir.recursive=true
在hive中,设置执行查问后,会提醒谬误,要求将set hive.mapred.supports.subdirectories=true;
通过两个set后,仔执行查问,hive即可对tez引擎产union产出的数据进行拜访。
同时为了查看tez的此种状况对其余引擎的兼容性,又对sparktThriftServer的sql拜访进行了测试:
对于sparkthriftserver,只需增加set mapreduce.input.fileinputformat.input.dir.recursive=true ,即可拜访对应数据
总结一下sparkThriftServer模式与hive的不同
1)在表目录下同时有文件和目录时(非分区目录)
此时sparkThriftServer和hive都会报非文件谬误
2)在表目录下均为目录(非分区目录)
此时sparkThriftServer会报非文件谬误,hive无错,然而查问为空
(4)解决方案
1)计划一:所有hive端都替换为tez引擎
这不会呈现上述问题,但会存在对其余引擎如spqrk,mr,presto等的影响
2)计划二:为hadoop和hive设置可递归读
该计划绝对可行,但须要调研对一般mr工作的影响