1.慎用API
大数据场景下不害怕数据量大,害怕的是数据倾斜,怎么样避免数据倾斜,找到可能产生数据倾斜的函数尤为关键.数据量较大的情况下,慎用count(distinct),count(distinct)容易产生数据倾斜哎.
2.自定义UDAD函数优化
sum,count,max,mix等UDAF函数,不怕数据倾斜问题,hadoop在map端汇总合并优化
3.设置合理的map reduce的task数量
3.1 map阶段优化
mapred.min.split.size:指的是数据的最小分割单元大小;min的默认值是1B;
mapred.max.split.size:指的是数据的最大分割单元大小;max的默认值是256M;
通过调整max可以起到调整map数的作用,减小max可以增加map数,增大max可以减少map数.
需要注意的是,直接调整mapred.map.tasks这个参数是没有效果的.
举例:
1) 假设input目录下有1个文件a,大小位780M,那么hadoop会将该文件a分割成7块(6个128M和1个12M的块),从而产生7个map数;
2) 假设input目录下有3个文件a,b,c,大小分别是10M,20M,130M,那么hadoop会分割成4块(10M,20M,128M,2M),从而生成4个map数
注意:如果文件大于块大小(128M),那么会拆分,如果小于块大小,则把该文件当成一个块.
其实这就是涉及到小文件的问题:如果一个任务有很多小文件(远远小于块大小128M),则每个小文件也会被当作一个块,用一个map任务来完成.
而一个map任务启动和初始化的时间远远大于逻辑处理时间.就会造成很大的资源浪费.而且,同时可执行的map数也是受限的.那么,是不是保证每个map处理接近128M的文件快,就OK了呢?答案也是不一定的,比如有一个127M的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也非常耗时.
解决办法:减少map数或者增加map数
减少map数量:
假设一个SQL任务: select count(1) from table_name where dt = ‘2021-08-25‘; 共有194个文件生成,其中很多是远远小于128M的小文件,总大小9G,正常执行会用194个map任务. Map总消耗的计算资源:SLOTS_MILLIS_MAPS= 623,020 通过一下方法来在map执行前合并小文件,减少map数 set mapred.max.split.size=100000000; set mapred.min.split.size.per.node=100000000; set mapred.min.split.size.per.rack =100000000; set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat ; # 开启map端的小文件合并 再执行上面的语句,用了74个map任务,map消耗的计算资源,SLOTS_MILLIS_MAPS= 333,500 注意:100000000等于100M 前面三个参数确定合并文件快的大小,大于文件快大小128M的,按照128M来分割,小于128M且大于100M的,按照100M分割,把那些小于100M的(包括小文件和分割大文件剩下的)进行合并,最终生成74个文件块.
增大map数量:
当input的文件很大时,任务逻辑复杂,map执行非常慢,可以考虑增加map数,来使得每个map处理的数据量减少,从而提高人任务的执行效率. 假设案例: select data_desc, count(1), count(distinct id), sum(case when ..) from table_name 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(几百万)的数据,效率肯定提高.
3.2 reduce阶段的优化
Reduce的个数对整个作业的运行性能有很大影响,如果Reduce设置的过大,那么将会产生很多小文件,对NameNode会产生一定的影响,而且整个作业的运行时间未必会减少,如果Reduce设置的过小,那么单个Reduce处理的数据将会很大,很可能引起OOM异常.
如果设置的mapred.reduce.tasks/mapreduce.job.reduces参数,那么hive会直接使用它的值作为Reduce的个数;如果mapred.reduce.tasks/mapreduce.job.reduces的值没有设置(也就是-1),那么Hive会根据输入文件的大小估算Reduce的个数,可能未必很准确,因为Reduce的输入是Map的输出,而Map的输出可能会比输入要小,所以最准确的数根据Map的输出估算Reduce的个数