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数据时,会自动根据分区字段的值,将数据插入到对应的分区中去;
- 开启动态分区
set hive.exec.dynamic.partition=true;
- 设置非严格模式(动态分区的模式默认为strict,表示必须指定至少一个分区为静态分区)
set hive.exec.dynamic.partition.mode=nonstrict;
- 在所有执行MR的节点上,最大一共可以创建多少个动态分区,默认1000
set hive.exec.max.dynamic.partitions=1000;
- 在每个执行MR的节点上,最大可以创建多少个动态分区,该字段需要根据实际数据来定,如:源数据该分区字段包含200个值,那么该参数需要设置成大于200,否则就会报错,默认100
set hive.exec.max.dynamic.partitions.pernode=200;
- 整个MR job中,最大可以创建多少个HDFS文件,默认100,000
set hive.exec.max.created.files=100000;
- 当有空分区生成时,是否抛出异常,默认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端聚合
- 是否在map端进行聚合,默认true:
set hive.map.aggr = true;
- 在map端进行聚合操作的条目数:
set hive.groupby.mapaggr.checkinterval = 100000;
- 有数据倾斜时进行负载均衡(默认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的数量处理数据倾斜;
- 通过hive参数调整reduce数:
-- 每个reduce处理的数据量(默认256M)
set hive.exec.reducers.bytes.per.reducer = 256000000;
-- 每个任务最大的reduce数(默认1009)
ser hive.exec.reducers.max = 1009;
- 通过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;