hive优化指北

建表优化

分区表

分区表对应一个HDFS上的独立文件夹,该文件夹下是该分区所有的数据文件,HIVE分区就是分目录;

  • 分区表创建:
create table table_name(
	xxxx
) partitioned by (ds string)

分区不能是表中已经存在的字段

  • 添加分区:
alter table table_name add partition(ds='xxx')
  • 删除分区:
-- 单个分区
alter table table_name drop partition(ds='xxxx')
-- 多个分区
alter table table_name drop partition(ds='xxxx'), partition(ds='xxxxx')

动态分区

对分区表进行insert数据时,会自动根据分区字段的值,将数据插入到对应的分区中去;

  1. 开启动态分区
set hive.exec.dynamic.partition=true;
  1. 设置非严格模式(动态分区的模式默认为strict,表示必须指定至少一个分区为静态分区)
set hive.exec.dynamic.partition.mode=nonstrict;
  1. 在所有执行MR的节点上,最大一共可以创建多少个动态分区,默认1000
set hive.exec.max.dynamic.partitions=1000;
  1. 在每个执行MR的节点上,最大可以创建多少个动态分区,该字段需要根据实际数据来定,如:源数据该分区字段包含200个值,那么该参数需要设置成大于200,否则就会报错,默认100
set hive.exec.max.dynamic.partitions.pernode=200;
  1. 整个MR job中,最大可以创建多少个HDFS文件,默认100,000
set hive.exec.max.created.files=100000;
  1. 当有空分区生成时,是否抛出异常,默认false
set hive.error.on.empty.partition=false;

分桶表

分区表是针对数据的存储路径,分桶是针对数据的文件;

  • 创建分桶表,按照id分为五个桶
create table table_name(
	xxxxx
)
clustered by (id)
into 5 buckets;

文件格式与压缩

hive支持的存储数据的格式主要有:textfile(行)、sequencefile(行)、orc(列)、parquet(列)等;

根据查询的类型选择合适的存储格式,能有效的提高查询速度,如:对于select *较多的查询可以选择行存储,对于select具体字段较多的场景可以选择列式存储;

HQL优化

列裁剪与分区裁剪

列裁剪就是在查询时只读取需要的列,分区裁剪就是只读取需要的分区

group by优化

默认情况下,map阶段同一key数据会分发给一个reduce,当一个key数据过大时就会数据倾斜,但是不是所有聚合操作都需要在reduce端完成,很多聚合操作都可以先在map端进行部分聚合,最后在reduce端得出最终结果

开启map端聚合

  1. 是否在map端进行聚合,默认true:
set hive.map.aggr = true;
  1. 在map端进行聚合操作的条目数:
set hive.groupby.mapaggr.checkinterval = 100000;
  1. 有数据倾斜时进行负载均衡(默认false)
set hive.groupby.skewindata=true;

开启map端聚合后,会生成两个MR任务,第一个MR中,map的输出结果会随机分布到reduce中,每个reduce做部分聚合操作并输出结果,这样处理的结果是相同的group by key有可能分发到不同的reduce中,从而实现负载均衡的目的;


第二个MR任务在根据处理的数据结果按照group by key分布到reduce中,保证相同的key分布到同一个reduce中,最后完成最终的聚合操作;

vectorization

vectorization:矢量计算,在计算类似scan、filter、aggregation的时候,设置批处理的增量大小 1024行/次 来达到比 单条记录/次 获得更高的效率;


开启参数:

set hive.vectorized.execution.enable = true;
set hive.vectorized.execution.reduce.enable = true;

多重模式

如果一堆sql,模式一样,都是从同一张表进行扫描,做不同的逻辑,如:

insert into table_b select xxx from table_a where id > 1;
insert into table_c select xxx from table_a where age > 2;
insert into table_d select xxx from table_a where xxxxx;
......

这种类型的sql有多少条,table_a就会被扫描多少次,修改为多重模式后,表只用扫描一次:

from table_a
insert into table_b select xxx where id > 1
insert into table_c select xxx where age > 2
insert into table_d select xxx where xxxxx
......

in/exists语句优化

in/exists语句:

select a.id, a.name from a where a.id in (select b.id from b);
select a.id, a.name from a where exists (select id from b where a.id = b.id)

使用join改写:

select a.id, a.name from a join b on a.id = b.id;

高效替代方案:

select a.id, a.name from a left semi join b on a.id = b.id

CBO优化

join的时候前面的表都会被加载到内存中,后面的表进行磁盘扫描

select a.*, b.*, c.* from a join b on a.id = b.id join c on a.id = c.id;

hive自0.14.0后添加了一项“cost based optimizer”来对HQL执行计划进行优化,这个功能通过 hive.cbo.enable来开启,在hive1.1.0后,默认为开启状态;它可以自动优化HQL中的多个join的顺序,并选择合适的jon算法;


CBO为成本优化器,hive在提供最终执行前,优化每个查询的执行逻辑和物理执行计划,这些优化工作是交给底层来完成的,根据查询成本执行进一步优化,从而产生潜在的不同决策:如何排序连接、执行哪种类型的连接、并行度等等;


开启CBO:

set hive.cbo.enable = true;
set hive.compute.query.using.stats = true;
set hive.stats.fetch.column.stats = true;
set hive.stats.fetch.partition.stats = true;

谓词下推

谓词下推是将sql中的where谓词逻辑都尽可能的提前执行,减少下游处理的数据量,对应的逻辑优化器是:PredicatePushDown


开启谓词下推(默认是true):

set hive.optimize.ppd = true;

注意:在开启CBO的前提下,关闭谓词下推,实际执行时还是会有谓词下推操作,这是因为CBO优化器自动选择了代价最小的执行计划,所以如需关闭谓词下推,则同时需要关闭CBO ;

MapJoin

MapJoin是将join双方比较小的表直接分发到各个map进程的内存中,在map进行过程中进行join操作,这样就不用进行reduce操作了。如果不指定MapJoin或者不符合MapJoin的条件,那么hive解析器会将join操作装换成common join,即在reduce阶段完成join操作;


开启MapJoin:

set hive.auto.convert.join = true;

大表小表阈值:

set hive.mapjoin.smalltable.filesize = 25000000;

注意:开启了MapJoin,只有大表join小表时才会走MapJoin,小表join大表还是会有reduce端join;

SMB join

SMB join即:sort merge bucket join,MapJoin适合大表join小表的场景,SMB join则适合大表join大表的场景;


未分桶时大表join大表,需将所有的数据都读取进来做join操作,而SMB join即将大表创建为一个个的分桶表,需要join的字段与分桶字段一致时,原来需要join的字段相同的值一定在同一个桶id的文件中,分桶后的大表join只需要将分桶后的文件根据桶id进行join操作后union在一起即可;


开启SMB join:

set hive.optimize.bucketmapjoin = true;
set hive.optimize.bucketmapjoin.sortedmerge = true;

笛卡尔积

join操作时不加on条件,或者on条件无效时,因为找不到join key,hive只能使用一个reduce来完成笛卡尔积,通过设置严格模式来禁止HQL中出现笛卡尔积查询;


开启hive严格模式:

set hive.mapred.mode=strict;

注意:开启严格模式后hive将禁止三种查询:1、查询分区表必须带分区;2、order by操作必须带limit;3、禁止笛卡尔积查询;


数据倾斜优化

数据倾斜是指某些reduce处理的数据量跟其他reduce处理的数据量差距巨大,导致绝大部分reduce很快完成,而少数reduce执行很慢甚至最终执行失败;


导致数据倾斜的原因在于按照key分组后,少量的任务负责了绝大部分的数据计算,也就是说HQL中一定存在分组操作,即HQL单表携带了group by查询或两张表join的查询;

单表数据倾斜优化

参数配置

当任务中存在group by操作的同时聚合函数为count或者sum时可以通过设置开启map端聚合参数来处理数据倾斜问题;

增加reduce数

对于多个key同时导致的数据倾斜,可以通过提高reduce的数量处理数据倾斜;

  1. 通过hive参数调整reduce数:
-- 每个reduce处理的数据量(默认256M)
set hive.exec.reducers.bytes.per.reducer = 256000000;
-- 每个任务最大的reduce数(默认1009)
ser hive.exec.reducers.max = 1009;
  1. 通过MR参数调整reduce数:
set mapreduce.job.reduces = 15;

join数据倾斜优化

如果确定是join出现的数据倾斜,可通过下列参数解决:

-- join的键对于的记录条数超过这个值则会进行拆分
set hive.skewjoin.key = 100000;
set hive.optimize.skewjoin = true;

在join过程中,会将计数超过阈值hive.skewjoin.key的倾斜key对于的行临时写进文件中,然后再启动另一个job做map join生成结果,通过参数还可以控制第二个job的mapper数量:

set hive.skewjoin.mapjoin.map.tasks = 10000;

任务优化

map优化

当input的文件都很大,任务逻辑复杂,map执行非常慢时,可以考虑增加map数来使得map处理的数据量减少,从而提高任务的执行效率;


hive map个数计算方式:max(minSize, min(maxSize, blockSize)),blockSize即为hdfs块大小,默认128m,而minSize为1,maxSize为Long.MAX_VALUE

所以可以通过配置maxSize大小来增加map数:

set mapreduce.input.fileinputformat.split.maxsize = 100;

小文件合并

对于map-only任务,设置任务结束时合并小文件,默认为true

set hive.merge.mapfiles = true;

对于map reduce任务,在任务结束时合并小文件:

-- 开启mapreduce结束后小文件合并
set hive.merge.mapredgiles = true;
--合并文件大小,默认256M
set hive.merge.size.per.task = 268435456;
-- 当输出文件的平均大小小于该值时,启动一个独立的任务处理文件合并
set hive.merge.smallfiles.avgsize = 16777216;

推测执行

推测执行意思是同时启动多个任务,谁先执行完成,就使用谁的结果,默认是开启状态

-- mr参数
set mapred.map.tasks.speculative.execution = true;
-- hive参数,效果与上面一样
set hive.mapred.reduce.tasks.speculative.execution = true;

reduce优化

设置合理的reduce数

配置参考数据倾斜增加reduce数配置

任务整体优化

fetch抓取

fetch抓取是指对于某些情况下的查询可以不必使用MR计算,如select * from table,这种情况下,hive可以简单的读取对于表的存储目录下的文件,然后输出;
开启fetch抓取:

set hive.fetch.task.conversion = more;
-- none:停用fetch抓取;
-- minimal:select、分区字段过滤、limit使用fetch抓取;
-- more:select、filter、limit使用fetch抓取

启用本地模式

有时输入数据量很小,为查询出发执行任务的时间可能会比实际job执行的时间要长,对于大多数情况,hive可以通过本地模式在单机上处理所有任务,对于数据量较小的情况下,执行时间可以明显被缩小


开启本地模式:

-- 开启本地mr
set hive.exec.mode.local.auto = true;
-- 设置local mr的最大输入数据量,当输入数据量小于这个值时,采用local mr的方式,默认128M
set hive.exec.mode.loacl.auto.inputbytes.max = 134217728;
-- 设置local mr的最大输入文件个数,当输入文件个数小于这个值时,采用local mr的方式,默认为4
set hive.exec.mode.loacl.auto.input.files.max = 4;

并行执行

hive会将一个查询转化为一个或者多个阶段,默认情况下,hive一次只会执行一个阶段,某些情况下,这些阶段可能并非完全依赖,也就是说有些阶段可以并行执行,那么job可能会越快完成;


开启并行执行:

-- 开启并行执行
set hive.exec.parallel = true;
-- 同一个sql运行的最大并行度,默认8
set hive.exec.parallel.thread.number = 8;

jvm重用


hadoop默认配置是使用派生JVM来执行map和reduce任务的,这是jvm的启动过程可能会造成相当大的开销,尤其是执行的job包含有成千上万个task任务的情况。JVM重用可以使得JVM实例在同一个JOB中重新使用N次


开启jvm重用:

set mapred.job.reuse.jvm.num.tasks = 10;
posted @ 2021-07-13 23:53  Sirius-  阅读(453)  评论(0编辑  收藏  举报