以最详细,生产实例的方式展现hive大量小文件的调优过程,包括:
小文件产生的原因
-
动态分区使用不合理,动态分区插入的时候,误判导致产生大量分区,大量分区自然就产生大量小文件
-
reduce的数量设置的较多,到reduce处理时,会分配到不同的reduce中,会产生大量的小文件
-
源数据文件本身就存在大量的小文件
小文件过多产生的影响
-
首先从底层存储HDFS文件系统来讲,HDFS本身就不适合存储大量小文件,小文件过多会致使namenode元数据特别大, 占用太多内存,严重影响HDFS的性能
-
对 hive 来讲,在进行查询时,每一个小文件都会当成一个块,启动一个Map task来完成,而一个Map task启动和初始化的时间远远大于逻辑处理的时间,就会形成很大的资源浪费。并且,同时可执行的Map task数量也是受限的。
hive distribute by分区函数应用调优
distribute by类似MR中partition(自定义分区),分区规则是根据分区字段的hash码与reduce的个数进行取余操作后,余数相同的分到一个区。
以表中字段分区
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=10000000;
set hive.mapjoin.smalltable.filesize=200000000;
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;
set hive.merge.rcfile.block.level=false;
--指定tmp.tmp_01的文件存储格式,默认为textfile
set hive.default.fileformat = orc;
drop table if exists tmp.tmp_01;
create table tmp.tmp_01
--STORED AS orc
as
SELECT
a.item_sku_id
FROM (
SELECT
item_sku_id,sku_name,brandname_cn,data_type
FROM big_table
WHERE dt = '2022-02-15' AND brandname_cn = '汾酒'
distribute by item_sku_id -- 大表以表中字段分组
) a --数据量远远大于10M
LEFT OUTER JOIN
( SELECT item_sku_id
FROM small_table
WHERE version = '1' AND normal_brandname_en = 'fenjiu' AND dt = '2022-02-15'
) b --数据量大小小于10M
ON a.item_sku_id = b.item_sku_id
WHERE b.item_sku_id IS NULL;
结果:
2022-02-16 23:03:47 INFO Table tmp.tmp_ljjtest02 stats: [numFiles=23, numRows=23, totalSize=5894, rawDataSize=2252]
2022-02-16 23:03:47 INFO Stage-1: Map: 18024 Reduce: 1009 Cumulative CPU: 377078.2 sec HDFS Read: 193.287 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 4m18s539ms job_5690061100801_32622232
2022-02-16 23:03:47 INFO Stage-10: Map: 920 Cumulative CPU: 5431.21 sec HDFS Read: 0.008 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 52s336ms job_5690061100801_32622707
2022-02-16 23:03:47 INFO Stage-4: Map: 832 Cumulative CPU: 1078.67 sec HDFS Read: 0.001 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 31s273ms job_8766029101801_36250210
2022-02-16 23:03:47 INFO Total MapReduce CPU Time Spent: 4d10h33m8s80ms
2022-02-16 23:03:47 INFO Total Map: 19776 Total Reduce: 1009
2022-02-16 23:03:47 INFO Total HDFS Read: 193.296 GB Written: 0.000 GB
结果分析:
以表中字段分区,如果数据不均匀,可能会造成数据倾斜。
以随机数分区
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=10000000;
set hive.mapjoin.smalltable.filesize=200000000;
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;
set hive.merge.rcfile.block.level=false;
--指定tmp.tmp_01的文件存储格式,默认为textfile
set hive.default.fileformat = orc;
drop table if exists tmp.tmp_01;
create table tmp.tmp_01
--STORED AS orc
as
SELECT
a.item_sku_id
FROM (
SELECT
item_sku_id,sku_name,brandname_cn,data_type
FROM big_table
WHERE dt = '2022-02-15' AND brandname_cn = '汾酒'
distribute by rand(100)-- 大表以随机数分组
) a --数据量远远大于10M
LEFT OUTER JOIN
( SELECT item_sku_id
FROM small_table
WHERE version = '1' AND normal_brandname_en = 'fenjiu' AND dt = '2022-02-15'
) b --数据量大小小于10M
ON a.item_sku_id = b.item_sku_id
WHERE b.item_sku_id IS NULL;
结果:
2022-02-16 22:53:00 INFO Table tmp.tmp_01 stats: [numFiles=3, numRows=23, totalSize=958, rawDataSize=2235]
2022-02-16 22:53:00 INFO Stage-1: Map: 18024 Reduce: 1009 Cumulative CPU: 372613.35 sec HDFS Read: 193.289 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 5m32s865ms job_5690061100801_32621522
2022-02-16 22:53:00 INFO Stage-10: Map: 924 Cumulative CPU: 5424.53 sec HDFS Read: 0.008 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 41s846ms job_5690061100801_32621870
2022-02-16 22:53:00 INFO Stage-4: Map: 858 Cumulative CPU: 1374.95 sec HDFS Read: 0.001 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 43s592ms job_5690061100801_32621913
2022-02-16 22:53:00 INFO Total MapReduce CPU Time Spent: 4d9h23m32s830ms
2022-02-16 22:53:00 INFO Total Map: 19806 Total Reduce: 1009
2022-02-16 22:53:00 INFO Total HDFS Read: 193.298 GB Written: 0.000 GB
结果分析:
以随机数分区,可以把数据从map端随机均匀的拆分给reduce端
总结
数据重分区(触发一个mr job),控制在map端如何拆分数据给reduce端,默认hash
distribute by item_sku_id 会导致数据倾斜
distribute by rand() 如果执行过程中其中一个reduce任务失败,可能会导致数据丢失或重复
distribute by rand(int seed) 指定随机种子,会得到一个稳定的随机数序列,可以避免数据不准确的问题
distribute by rand()的风险具体原理如下:
-
部分reduce shuffle失败导致上游个别map task重新生成数据,如果shuffle分片逻辑不包含随机因子,会导致新生成的数据shuffle分片与之前不完全一致,进而导致部分数据重复读取或者数据丢失
-
reduce task从每个map task的结果文件中拉取对应分区的数据。数据在map阶段已经是分好区了,并且会有一个额外的索引文件记录每个分区的起始偏移量。所以reducetask取数的时候直接根据偏移量去拉取数据。
-
新生成的shuffle分片内数据会丢数据,同时也可能包含已经分发到其他reducer的map阶段数据,导致数据重复分发。
注意:
rand(int seed) 是个稳定的随机数序列而不是固定的数值,从table列的角度来说,每一列都是不一样
可以通过【select *,rand(1) as rand from t1】去验证
hive输入端小文件合并调优方式
输入端小文件合并设置:
--输入小文件合并,若读取小文件较多,则设置在map端进行小文件合并参数
set hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;-- 执行Map前进行小文件合并
set hive.hadoop.supports.splittable.combineinputformat = true;
--其中一种方版本
set mapreduce.input.fileinputformat.split.maxsize = 256000000;
set mapreduce.input.fileinputformat.split.minsize =256000000;
set mapreduce.input.fileinputformat.split.minsize.per.node=256000000;
set mapreduce.input.fileinputformat.split.minsize.per.rack=256000000;
--另一种版本
SET mapred.max.split.size = 256000000; //设置mapper拆分大小
SET mapred.min.split.size = 256000000;
SET mapred.min.split.size.per.node = 256000000;
SET mapred.min.split.size.per.rack = 256000000;
根据hive的版本不同,不同的版本可能有些配置不一样,然后根据自己的实际集群情况进行设置。
map-only 和 mapReduce job输出小文件合并调优方式
开启输出结果小文件合并:
rcfile和orc需要特殊设置才能进行文级别的合并,parquet、textfile、sequencefile默认进行文件级别的合并
set hive.merge.mapfiles = true;--开启只有map(map-only)操作结束时小文件输出合并
set hive.merge.mapredfiles = true;--开启有map和reduce操作结束时小文件输出合并
set hive.merge.size.per.task = 256000000;--合并后每个文件的大小
set hive.merge.smallfiles.avgsize=256000000;--当输出文件的平均大小小于256000000时,启动一个独立的MR任务进行文件合并,触发小文件合并的阀值
set hive.merge.orcfile.stripe.level=false; --当这个参数设置为true,orc文件进行stripe Level级别的合并,当设置为false,orc文件进行文件级别的合并,默认为true。
set hive.merge.rcfile.block.level=false;--当这个参数设置为true,rcfile文件进行block Level级别的合并,当设置为false,rcfile文件进行文件级别的合并,默认为true。
map-only操作结束时小文件合并
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=10000000;
set hive.mapjoin.smalltable.filesize=200000000;
set hive.merge.mapfiles = true; -- map-only结束时小文件合并
set hive.merge.mapredfiles = false; -- MR操作结束时,不合并小文件
set hive.merge.size.per.task = 256000000;
set hive.merge.smallfiles.avgsize = 256000000;
set hive.merge.orcfile.stripe.level=false;
set hive.merge.rcfile.block.level=false;
--指定tmp.tmp_01的文件存储格式,默认为textfile
set hive.default.fileformat = orc;
drop table if exists tmp.tmp_01;
create table tmp.tmp_01
--STORED AS orc
as
create table tmp.tmp_01 as
SELECT
item_sku_id
FROM
cdm.cdm_goods_360buy_act_basic_da
WHERE
dt = '2022-02-15'
AND barndname_cn = '汾酒'
结果:
2022-02-17 14:45:37 INFO Table tmp.tmp_ljjtest021 stats: [numFiles=1, numRows=18415, totalSize=110953, rawDataSize=1762456]
结果分析:
【set hive.merge.mapfiles = true;】控制map-only操作结束时的小文件合并
mapReduce操作结束时小文件合并
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=10000000;
set hive.mapjoin.smalltable.filesize=200000000;
set hive.merge.mapfiles = false; -- map-only结束时小文件不合并
set hive.merge.mapredfiles = true; -- MR操作结束时,合并小文件
set hive.merge.size.per.task = 256000000;
set hive.merge.smallfiles.avgsize = 256000000;
set hive.merge.orcfile.stripe.level=false;
set hive.merge.rcfile.block.level=false;
--指定tmp.tmp_01的文件存储格式,默认为textfile
set hive.default.fileformat = orc;
drop table if exists tmp.tmp_01;
create table tmp.tmp_01
--STORED AS orc
as
SELECT
a.item_sku_id
FROM (
SELECT
item_sku_id,sku_name,brandname_cn,data_type
FROM big_table
WHERE dt = '2022-02-15' AND brandname_cn = '汾酒'
) a --数据量远远大于10M
LEFT OUTER JOIN
( SELECT item_sku_id
FROM small_table
WHERE version = '1' AND normal_brandname_en = 'fenjiu' AND dt = '2022-02-15'
) b --数据量大小小于10M
ON a.item_sku_id = b.item_sku_id
WHERE b.item_sku_id IS NULL;
结果:
2022-02-16 23:12:45 INFO Table tmp.tmp_01 stats: [numFiles=1, numRows=23, totalSize=426, rawDataSize=2235]
结果分析:
【set hive.merge.mapredfiles = true;】控制mapReduce和mapjoin操作结束时的小文件合并
总结
这里需要注意mapjoin操作,虽然只触发了map操作,而没有触发reduce操作。
但这是优化的结果,本质上还是mapReduce任务,因此被【set hive.merge.mapredfiles = true;】参数控制。
减少Reduce数量小文件的调优方式
reduce 的个数决定了输出的文件的个数,因此能够调整reduce的个数控制hive表的文件数量,
hive中的分区函数 distribute by 控制MR中partition分区,即reduce的数量,结合分区函数让数据更加均衡的分配到每一个reduce。
-
设置reduce的数量有两种方式,第一种是直接设置reduce个数
set mapreduce.job.reduces=10; -
第二种是设置每一个reduce的大小,Hive会根据shuffle后数据量总的大小确定reducer个数
set hive.exec.reducers.bytes.per.reducer=5120000000; – 默认是1G,设置为5G
输出结果减少小文件的数量
set mapreduce.job.reduces=10;
insert overwrite table A partition(dt)
select * from B
distribute by rand(1);
解释: 如设置reduce数量为10,则使用 rand(1), 随机生成一个数 x % 10 ,
这样数据就会随机进入 reduce task中,防止出现有的文件太大或太小
中间过程减少小文件的数量
set hive.exec.reducers.bytes.per.reducer=5120000000; -- 默认是1G,设置为5G
SELECT
a.*
FROM (
SELECT
item_sku_id,sku_name,brandname_cn,data_type
FROM big_table
WHERE dt = '2022-02-15' AND brandname_cn = '汾酒'
distribute by rand(1) -- 分组
) a --数据量远远大于10M
LEFT OUTER JOIN
( SELECT item_sku_id
FROM small_table
WHERE version = '1' AND normal_brandname_en = 'fenjiu' AND dt = '2022-02-15'
) b --数据量大小小于10M
ON a.item_sku_id = b.item_sku_id
WHERE b.item_sku_id IS NULL
解释: 如果map 到reduce端中间过程shuffle后总的数据量大小为50G,则分区数为10,则使用 rand(1), 随机生成一个数 x % 10 ,这样数据就会随机进入 reduce task中,防止中间过程的文件太大或太小
hive输出结果小文件问题调优思路
执行mapjoin,不合并小文件,不重分区
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=10000000;
set hive.mapjoin.smalltable.filesize=200000000;
set hive.merge.mapfiles = false;
set hive.merge.mapredfiles = false; --MR操作结束时,不合并小文件
set hive.merge.size.per.task = 256000000;
set hive.merge.smallfiles.avgsize = 256000000;
set hive.merge.orcfile.stripe.level=false;
set hive.merge.rcfile.block.level=false;
--指定tmp.tmp_01的文件存储格式,默认为textfile
set hive.default.fileformat = orc;
drop table if exists tmp.tmp_01;
create table tmp.tmp_01
--STORED AS orc
as
SELECT
a.item_sku_id
FROM (
SELECT
item_sku_id,sku_name,brandname_cn,data_type
FROM big_table
WHERE dt = '2022-02-15' AND brandname_cn = '汾酒'
--distribute by rand(1) -- 不分组
) a --数据量远远大于10M
LEFT OUTER JOIN
( SELECT item_sku_id
FROM small_table
WHERE version = '1' AND normal_brandname_en = 'fenjiu' AND dt = '2022-02-15'
) b --数据量大小小于10M
ON a.item_sku_id = b.item_sku_id
WHERE b.item_sku_id IS NULL;
结果:
2022-02-17 16:53:18 INFO Table tmp.tmp_01 stats: [numFiles=18020, numRows=23, totalSize=887747, rawDataSize=2252]
2022-02-17 16:53:18 INFO MapReduce Jobs Launched:
2022-02-17 16:53:19 INFO Stage-4: Map: 18020 Cumulative CPU: 205038.93 sec HDFS Read: 193.386 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 3m28s419ms job_8766029101801_36451400
2022-02-17 16:53:19 INFO
2022-02-17 16:53:19 INFO Total MapReduce CPU Time Spent: 2d8h57m18s930ms
2022-02-17 16:53:19 INFO Total Map: 18020 Total Reduce: 0
2022-02-17 16:53:19 INFO Total HDFS Read: 193.386 GB Written: 0.000 GB
结果分析:
执行mapjoin(在map端关联聚合),不合并小文件,每一个map对应一个输出文件。输出结果出现大量小文件。
执行mapjoin,不合并小文件,大表重分区
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=10000000;
set hive.mapjoin.smalltable.filesize=200000000;
set hive.merge.mapfiles = true;
set hive.merge.mapredfiles = false; --MR操作结束时,不合并小文件
set hive.merge.size.per.task = 256000000;
set hive.merge.smallfiles.avgsize = 256000000;
set hive.merge.orcfile.stripe.level=false;
set hive.merge.rcfile.block.level=false;
--指定tmp.tmp_01的文件存储格式,默认为textfile
set hive.default.fileformat = orc;
drop table if exists tmp.tmp_01;
create table tmp.tmp_01
--STORED AS orc
as
SELECT
a.item_sku_id
FROM (
SELECT
item_sku_id,sku_name,brandname_cn,data_type
FROM big_table
WHERE dt = '2022-02-15' AND brandname_cn = '汾酒'
--distribute by rand(1) -- 大表分组
) a --数据量远远大于10M
LEFT OUTER JOIN
( SELECT item_sku_id
FROM small_table
WHERE version = '1' AND normal_brandname_en = 'fenjiu' AND dt = '2022-02-15'
) b --数据量大小小于10M
ON a.item_sku_id = b.item_sku_id
WHERE b.item_sku_id IS NULL;
结果:
2022-02-16 22:53:00 INFO Table tmp.tmp_01 stats: [numFiles=3, numRows=23, totalSize=958, rawDataSize=2235]
2022-02-16 22:53:00 INFO Stage-1: Map: 18024 Reduce: 1009 Cumulative CPU: 372613.35 sec HDFS Read: 193.289 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 5m32s865ms job_5690061100801_32621522
2022-02-16 22:53:00 INFO Stage-10: Map: 924 Cumulative CPU: 5424.53 sec HDFS Read: 0.008 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 41s846ms job_5690061100801_32621870
2022-02-16 22:53:00 INFO Stage-4: Map: 858 Cumulative CPU: 1374.95 sec HDFS Read: 0.001 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 43s592ms job_5690061100801_32621913
2022-02-16 22:53:00 INFO Total MapReduce CPU Time Spent: 4d9h23m32s830ms
2022-02-16 22:53:00 INFO Total Map: 19806 Total Reduce: 1009
2022-02-16 22:53:00 INFO Total HDFS Read: 193.298 GB Written: 0.000 GB
结果分析:
执行mapjoin(在map端关联聚合),不合并小文件,大表预先分组,触发MR,减少mapjoin阶段输出的map数量(即当前job执行完成产生的中间结果文件数量)。由于输入map tasks少了, 输出结果小文件也大大减少。
执行mapjoin,合并小文件
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=10000000;
set hive.mapjoin.smalltable.filesize=200000000;
set hive.merge.mapfiles = true;
set hive.merge.mapredfiles = true; --MR操作结束时,合并小文件
set hive.merge.size.per.task = 256000000;
set hive.merge.smallfiles.avgsize = 256000000;
set hive.merge.orcfile.stripe.level=false;
set hive.merge.rcfile.block.level=false;
--指定tmp.tmp_01的文件存储格式,默认为textfile
set hive.default.fileformat = orc;
drop table if exists tmp.tmp_01;
create table tmp.tmp_01
--STORED AS orc
as
SELECT
a.item_sku_id
FROM (
SELECT
item_sku_id,sku_name,brandname_cn,data_type
FROM big_table
WHERE dt = '2022-02-15' AND brandname_cn = '汾酒'
--distribute by rand(1) -- 不分组,因为此时分组毫无意义,只会徒增任务开销
) a --数据量远远大于10M
LEFT OUTER JOIN
( SELECT item_sku_id
FROM small_table
WHERE version = '1' AND normal_brandname_en = 'fenjiu' AND dt = '2022-02-15'
) b --数据量大小小于10M
ON a.item_sku_id = b.item_sku_id
WHERE b.item_sku_id IS NULL;
结果:
2022-02-16 23:12:45 INFO Table tmp.tmp_01 stats: [numFiles=1, numRows=23, totalSize=426, rawDataSize=2235]
2022-02-16 23:12:45 INFO Stage-1: Map: 18024 Reduce: 1009 Cumulative CPU: 365693.05 sec HDFS Read: 193.289 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 3m21s803ms job_5690061100801_32623163
2022-02-16 23:12:45 INFO Stage-10: Map: 918 Cumulative CPU: 5232.29 sec HDFS Read: 0.008 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 40s544ms job_8766029101801_36250732
2022-02-16 23:12:45 INFO Stage-4: Map: 1 Cumulative CPU: 13.95 sec HDFS Read: 0.000 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 43s762ms job_8766029101801_36250769
2022-02-16 23:12:45 INFO Total MapReduce CPU Time Spent: 4d7h2m19s290ms
2022-02-16 23:12:45 INFO Total Map: 18943 Total Reduce: 1009
2022-02-16 23:12:45 INFO Total HDFS Read: 193.297 GB Written: 0.000 GB
结果分析:
执行mapjoin(在map端关联聚合),合并小文件。输出结果小文件数量同样也大大减少。
总结
mapjoin会在小表与大表join时的优化提升还是很明显的,但使用时有一些问题需要注意。
上例中执行mapjoin, 有18020个map,就对应输出18020个小文件(执行mapReduce会在大大减少输出的小文件数,测试集群默认最大的reduce数量1009个)。
因此就需要考虑后期小文件的合并,在大量小文件的场景下,需要考虑的处理方式是开启小文件合并或者大表重分区
hive中间过程(两个job之间)大量小文件的调优思路
不重分区,不合并小文件
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=10000000;
set hive.mapjoin.smalltable.filesize=200000000;
set hive.merge.mapfiles = false;
set hive.merge.mapredfiles = false; --MR操作结束时,不合并小文件
set hive.merge.size.per.task = 256000000;
set hive.merge.smallfiles.avgsize = 256000000;
set hive.merge.orcfile.stripe.level=false;
set hive.merge.rcfile.block.level=false;
WITH tmp as
(
SELECT
a.*
FROM (
SELECT
item_sku_id,sku_name,brandname_cn,data_type
FROM big_table
WHERE dt = '2022-02-15' AND brandname_cn = '汾酒'
--distribute by rand(1) -- 不分组
) a --数据量远远大于10M
LEFT OUTER JOIN
( SELECT item_sku_id
FROM small_table
WHERE version = '1' AND normal_brandname_en = 'fenjiu' AND dt = '2022-02-15'
) b --数据量大小小于10M
ON a.item_sku_id = b.item_sku_id
WHERE b.item_sku_id IS NULL
)
create table tmp.tmp_01
as
SELECT
c.item_sku_id
FROM tmp c
join small_table d
on c.item_sku_id=d.item_sku_id
结果:
2022-02-17 20:49:21 INFO Stage-8: Map: 18026 Cumulative CPU: 210204.23 sec HDFS Read: 193.388 GB HDFS Write: 0.001 GB SUCCESS Elapsed : 2m55s772ms job_5690061100801_32863650
2022-02-17 20:49:21 INFO Stage-6: Map: 472 Cumulative CPU: 3330.08 sec HDFS Read: 0.334 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 50s42ms job_5690061100801_32865433
2022-02-17 20:49:21 INFO Total MapReduce CPU Time Spent: 2d11h18m54s310ms
2022-02-17 20:49:21 INFO Total Map: 18498 Total Reduce: 0
2022-02-17 20:49:21 INFO Total HDFS Read: 193.722 GB Written: 0.001 GB
2022-02-17 20:49:22 INFO Time taken: 1045.892 seconds, Fetched: 46 row(s)
2022-02-17 20:49:23 INFO 任务结束! sql执行耗时 0 hour 17 mins 38 s
结果分析:
不重分区,不合并小文件的情况下用时1045秒
不重分区,合并小文件
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=10000000;
set hive.mapjoin.smalltable.filesize=200000000;
set hive.merge.mapfiles = true;
set hive.merge.mapredfiles = true; --MR操作结束时,合并小文件
set hive.merge.size.per.task = 256000000;
set hive.merge.smallfiles.avgsize = 256000000;
set hive.merge.orcfile.stripe.level=false;
set hive.merge.rcfile.block.level=false;
WITH tmp as
(
SELECT
a.*
FROM (
SELECT
item_sku_id,sku_name,brandname_cn,data_type
FROM big_table
WHERE dt = '2022-02-15' AND brandname_cn = '汾酒'
--distribute by rand(1) -- 不分组
) a --数据量远远大于10M
LEFT OUTER JOIN
( SELECT item_sku_id
FROM small_table
WHERE version = '1' AND normal_brandname_en = 'fenjiu' AND dt = '2022-02-15'
) b --数据量大小小于10M
ON a.item_sku_id = b.item_sku_id
WHERE b.item_sku_id IS NULL
)
create table tmp.tmp_01
as
SELECT
c.item_sku_id
FROM tmp c
join small_table d
on c.item_sku_id=d.item_sku_id
结果:
2022-02-17 20:45:25 INFO Stage-8: Map: 18026 Cumulative CPU: 214628.08 sec HDFS Read: 193.388 GB HDFS Write: 0.001 GB SUCCESS Elapsed : 3m41s147ms job_8766029101801_36477823
2022-02-17 20:45:25 INFO Stage-6: Map: 472 Cumulative CPU: 3995.4 sec HDFS Read: 0.334 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 59s677ms job_5690061100801_32865009
2022-02-17 20:45:25 INFO Total MapReduce CPU Time Spent: 2d12h43m43s480ms
2022-02-17 20:45:25 INFO Total Map: 18498 Total Reduce: 0
2022-02-17 20:45:25 INFO Total HDFS Read: 193.722 GB Written: 0.001 GB
2022-02-17 20:45:27 INFO Time taken: 1074.534 seconds, Fetched: 46 row(s)
2022-02-17 20:45:27 INFO 任务结束! sql执行耗时 0 hour 18 mins 10 s
结果分析:
不重分区,合并小文件的情况下用时1074秒
重分区,合并小文件
set hive.auto.convert.join=true;
set hive.auto.convert.join.noconditionaltask=true;
set hive.auto.convert.join.noconditionaltask.size=10000000;
set hive.mapjoin.smalltable.filesize=200000000;
set hive.merge.mapfiles = true;
set hive.merge.mapredfiles = true; --MR操作结束时,合并小文件
set hive.merge.size.per.task = 256000000;
set hive.merge.smallfiles.avgsize = 256000000;
set hive.merge.orcfile.stripe.level=false;
set hive.merge.rcfile.block.level=false;
WITH tmp as
(
SELECT
a.*
FROM (
SELECT
item_sku_id,sku_name,brandname_cn,data_type
FROM big_table
WHERE dt = '2022-02-15' AND brandname_cn = '汾酒'
distribute by rand(1) -- 分组
) a --数据量远远大于10M
LEFT OUTER JOIN
( SELECT item_sku_id
FROM small_table
WHERE version = '1' AND normal_brandname_en = 'fenjiu' AND dt = '2022-02-15'
) b --数据量大小小于10M
ON a.item_sku_id = b.item_sku_id
WHERE b.item_sku_id IS NULL
)
create table tmp.tmp_01
as
SELECT
c.item_sku_id
FROM tmp c
join small_table d
on c.item_sku_id=d.item_sku_id
结果:
2022-02-17 19:57:57 INFO Stage-1: Map: 18026 Reduce: 1009 Cumulative CPU: 362544.56 sec HDFS Read: 193.290 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 4m55s871ms job_5690061100801_32857972
2022-02-17 19:57:57 INFO Stage-9: Map: 928 Cumulative CPU: 5520.28 sec HDFS Read: 0.008 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 2m33s752ms job_5690061100801_32858541
2022-02-17 19:57:57 INFO Stage-7: Map: 472 Cumulative CPU: 3638.63 sec HDFS Read: 0.334 GB HDFS Write: 0.000 GB SUCCESS Elapsed : 1m42s589ms job_5690061100801_32858927
2022-02-17 19:57:57 INFO Total MapReduce CPU Time Spent: 4d7h15m3s470ms
2022-02-17 19:57:57 INFO Total Map: 19426 Total Reduce: 1009
2022-02-17 19:57:57 INFO Total HDFS Read: 193.634 GB Written: 0.000 GB
2022-02-17 19:57:58 INFO Time taken: 690.782 seconds, Fetched: 46 row(s)
2022-02-17 19:57:58 INFO 任务结束! sql执行耗时 0 hour 11 mins 44 s
结果分析:
重分区,合并小文件的情况下用时690秒
总结
在hive任务中间结果产生大量小文件的情况下,合并小文件设置对中间结果几乎没有影响;
但触发大表重分区,减少mapjoin阶段的map数量,会大大减少计算任务的用时
其他调优方式
压缩文件的处理
对于输出结果为压缩文件形式存储的状况,要解决小文件问题。若是在map输入前合并,对输出的文件存储格式并无限制;可是若是使用输出合并,则必须配合SequenceFile来存储,不然没法进行合并。
如下是实例:
set hive.exec.compress.output=true; --开启hive最终输出数据压缩功能
set mapred.output.compression.type=BLOCK;
set mapred.output.compression.codec=org.apache.hadoop.io.compress.SnappyCodec;
drop table if exists tmp.tmp_02;
create table tmp.tmp_02
STORED AS SEQUENCEFILE
AS
select *
from tmp.tmp_01
使用HAR归档文件
Hadoop的归档文件格式也是解决小文件问题的方式之一,并且hive提供了原生支持。
set hive.archive.enabled= true;
set hive.archive.har.parentdir.settable= true;
set har.partfile.size=1099511627776;
ALTER TABLE tmp_01 tARCHIVE PARTITION(ds='2008-04-08', hr='12' );
ALTER TABLE tmp_01UNARCHIVE PARTITION(ds='2008-04-08', hr='12' );