HIVE优化:语句、参数、表结构优化

文章目录

      • map相关配置
      • reduce相关配置
      • 表结构
        • 分区
        • 分桶
        • 索引
        • 列筛选
      • 语句优化
        • 大表join小表:Replication
        • count(distinct)
        • 表过滤
      • 参数优化
        • with语句
        • 向量运算
        • Hint
        • mapjoin
        • map端预聚合
        • 文件压缩
        • 合并小文件
        • 作业并行
        • 相关性优化
        • 本地执行
        • Fetch抓取
        • 其他
      • 连接优化器
      • 其他相关
        • 查看SQL执行计划
        • show:查看信息
        • desc:描述库表信息
        • load:数据加载
        • export命令

show conf 值名:查看配置项当前的值,如 show conf 'hive.execution.engine'

map相关配置

  • 在默认情况下Map的个数defaultNum=目标文件或数据的总大小totalSize/hdfs集群文件块的大小blockSize。
  • 当用户指定mapred.map.tasks,即为用户期望的Map大小,用expNum表示,这个期望值计算引擎不会立即采纳,它会获取mapred.map.tasks与defaultNum的较大值,用expMaxNum表示,作为待定选项。
  • 获取文件分片的大小和分片个数,分片大小为参数mapred.min.split.size 和blockSize间的较大值,用splitMaxSize表示,将目标文件或数据的总大小除以splitMaxSize即为真实的分片个数,用realSplitNum表示。
  • 获取realSplitNum与expMaxNum较小值则为实际的Map个数。

减少Map个数,需要增大mapred.min.split.size的值,减少mapred.map.tasks的值;
增大Map个数,需要减少mapred.min.split.size的值,同时增大mapred.map.tasks的值。


reduce相关配置

  • mapred.reduce.tasks:设置Reducer的数量,默认值是-1,代表有系统根据需要自行决定Reducer的数量。
  • hive.exec.reducers.bytes.per.reducer:设置每个Reducer所能处理的数据量,在Hive 0.14版本以前默认是1000000000(1GB), Hive 0.14及之后的版本默认是256MB。输入到Reduce的数据量有1GB,那么将会拆分成4个Reducer任务。
  • hive.exec.reducers.max:设置一个作业运行的最大Reduce个数,默认值是999。
  • hive.multigroupby.singlereducer:表示如果一个SQL 语句中有多个分组聚合操作,且分组是使用相同的字段,那么这些分组聚合操作可以用一个作业的Reduce完成,而不是分解成多个作业、多个Reduce完成。这可以减少作业重复读取和Shuffle的操作。
  • hive.mapred.reduce.tasks.speculative.execution:表示是否开启Reduce 任务的推测执行。即系统在一个Reduce 任务中执行进度远低于其他任务的执行进度,会尝试在另外的机器上启动一个相同的Reduce任务。
  • hive.optimize.reducededuplication:表示当数据需要按相同的键再次聚合时,则开启这个配置,可以减少重复的聚合操作。
  • hive.vectorized.execution.reduce.enabled:表示是否启用Reduce任务的向量化执行模式,默认是true。MapReduce计算引擎并不支持对Reduce阶段的向量化处理。
  • hive.vectorized.execution.reduce.groupby.enabled:表示是否移动Reduce任务分组聚合查询的向量化模式,默认值为true。MapReduce计算引擎并不支持对Reduce阶段的向量化处理。

表结构

分区

where条件是在map端过滤,分区筛选在输入阶段过滤;在hdfs上一个分区对应一个目录;对常用字段使用分区字段,比如日期,筛选某天的数据速度显著提升;

分桶

分桶能够对原有表或者分区所存储的数据进行重新组织,使得通过分桶的方式能够快速过滤掉大量不需要遍历的文件。分桶是对文件过滤,一般使用hash模余,每个记录存储到桶的算法:记录所存储的桶=mod(hash(分桶列的值),4)
hash表示Hash函数,获取分桶列的值对应的哈希值;mod表示取余函数。
如果查询字段为分桶字段,能快速定位条件所在记录位置,而无须全表扫表,类似索引。对于大表,可以快速缩短读取数据时间,同时也能优化表的链接。比如在两表JOIN中,相同桶的数据进行join可以节约时间。(不是一个桶的数据join不上)
可以使用hdfs dfs -ls 表路径查看表在HDFS的存储。
使用桶的Map连接要保证连接的两张表的分桶数之间是倍数关系。
如果两个表的某字段分桶是10个,在两个表使用该字段关联join时,启用6个reduce

索引

Hive的索引在Hive 3.0版本中被废弃,可以使用两种方式进行替换:

  • 物化视图(Materialized views)。这个概念对于使用Oracle的开发者并不陌生,通过使用物化视图,可以达到类似hive索引的效果,该特性在Hive 2.3.0版本中引入。
  • 使用ORC/Parquet的文件存储格式,也能够实现类似索引选择性扫描,快速过滤不需要遍历的block,这是比分桶更为细粒度的过滤。

列筛选

ORC/Parquet中存储了文件定义的Schema, ORC/Parquet可以通过Schema直接读取表所在的列,以达到列过滤的目的。
在spark中,rdd需要对每一行按分隔符分割筛选,DataFrame有表结构可直接筛选对应数据列。


语句优化

大表join小表:Replication

HIVE中表连接的两种方式,Repartition连接和Replication连接。
Repartition连接
发生在Shuffle和Reduce阶段。一般如果不特别做其他声明,通常提到的连接就是Repartition连接。Map的任务读取A、B两个表的数据,将按连接条件发往相同的Reduce,在Reduce中计算合并的结果。

Replication连接
发生在Map阶段,Replication连接在Map阶段完成连接操作,相比发生在Shuffle阶段的Repartition连接,可以减少从HDFS读取表的次数,可以在Map 阶段实现连接时不匹配条件的记录行的过滤,减少下游网络传输的数据量和下游计算节点处理的数据量。
Replication 连接在操作时会将一个表的数据复制到各个Map 任务所在的节点并存储在缓存中,如果连接的两个表都是数据量庞大的表,会带来较大的性能问题,仅适用于两表连接中有一张小表的情况。
Replication连接根据实现的不同表连接可以分为:

  • 普通的MapJoin:对使用的表类型无特殊限制,只需要配置相应的Hive配置。
  • Bucket MapJoin:要求使用的表为桶表。hash模余相同的值会被分发到同一个桶。
  • Skewed MapJoin:要求使用的表为倾斜表。
  • Sorted Merge Bucket MapJoin:要求使用的表为桶排序表。

普通mapjoin

-- hive命令可能被禁用,这里开启
set hive.ignore.mapjoin.hint=false;
select /*+mapjoin(t2)*/ t2.product_type
	,sum(t1.salses) as sum_sale
from sales_table t1 
join dim_product_info t2 
on t1.product_id = t2.product_id
group by t2.product_type

在Hive中使用common map join有几种方式,方式一是使用MapJoin的hint语法。需要注意的是要关闭忽略hint的配置项,否则该方法不会生效,即set hive.ignore.mapjoin.hint=false;

可使用Hive配置MapJoin。使用Hive配置需要使用到以下配置:

  • hive.auto.convert.join:在Hive 0.11版本以后,默认值为true,表示是否根据文件大小将普通的repartition连接将化为Map的连接。
  • hive.smalltable.filesize/hive.mapjoin.smalltable.filesize:默认值为25000000(bytes)。两个配置表示的含义都是当小表的数据小于该配置指定的阀值时,将尝试使用普通repartition连接转化Map连接。该配置需要和hive.auto.convert.join配合使用。

Map Join相关的Hive配置如下:

  • hive.mapjoin.localtask.max.memory.usage:默认值为0.9。表示小表保存到内存的哈希表的数据量最大可以占用到本地任务90%的内存,如果超过该值,则表示小表的数据量太大,无法保存到内存中。
  • hive.mapjoin.followby.gby.localtask.max.memory.usage:默认值是0.55。表示如果在MapJoin之后还有group by的分组聚合操作,本地任务最大可以分配当前任务55%的内存给哈希表缓存数据,如果缓存的数据大于该值,表示停止当前本地任务。在优化时,如果分组聚合后的数据会大幅度地缩小,可以适当提高该阀值,以提升内存可以缓存的数据量,如果分组聚合后的数据不降反增,则需要适当调低该比值防止内存溢出,导致作业失败。

倾斜连接:

set hive.optimize.skewjoin=true;
-- 负载均衡参数
set hive.skewjoin.key=100000;
select  t2.product_type
	,t1.salses
from sales_table t1 
join dim_product_info t2 
on t1.product_id = t2.product_id

创建倾斜表:通过在创建表时指定数据倾斜键,将指定的数据键分割成单独的数据文件或者目录,这样可以加快数据过滤从而提供作业的运行速度。

--创建倾斜表student_list_bucket表
CREATE TABLE student_info_bucket (s_no STRING, s_score bigint)
--指定倾斜的键
SKEWED BY (s_score) ON (96,97)
--以目录形式存储倾斜的数据
STORED AS DIRECTORIES;

倾斜键的数据存储在Hive_DEFAULT_LIST_BUCKETING_DIR_NAME目录中(比如上面的96和97),而其他数据则存储在与该目录同一级的文件目录下。
Hive中与SkewedJoin相关的配置如下:

  • hive.optimize.skewjoin:默认值是false,表示是否优化有倾斜键的表连接。如果为true, Hive将为连接中的表的倾斜键创建单独的计划。
  • hive.skewjoin.key:默认值为100000。如果在进行表连接时,相同键的行数多于该配置所指定的值,则认为该键是倾斜连接键。
  • hive.skewjoin.mapjoin.map.tasks:默认值为10000。倾斜连接键,在做MapJoin 的Map任务个数。需要与hive.skewjoin.mapjoin.min.split一起使用。
  • hive.skewjoin.mapjoin.min.split:默认值为33554432,即32MB。指定每个split块最小值,该值用于控制倾斜连接的Map任务个数。

count(distinct)

语句1:
select count(distinct age) as dis_cnt from user_info
count(distinct)会将同一个key的数据交给一个reduce处理,数据数据记录行多,跑批相对耗时长。以上语句由一个MR处理完成。
语句2:

select count(1) as dis_cnt 
from (select age from user_info group by age) t

语句2执行计划由两个MR构成;
age年龄枚举值较少,map段预聚合,shuffle到reduce端数据量整体还是比较少,两者区别不是很明显。count(distinct)计算使用一个MR计算耗时可能会更短些。
如果key数据量比较大的情况下,两个MR处理相对会快些,比如大企业用户规模较大,查看近1年的用户活跃数(月表,用户id去重),如果使用count(distinct),如果每个月活跃去重在1亿,一年意味着大概有12亿的数据交由1个reduce去重计数。这时候交由两个MR处理,多处一个MR的开销相比时效提升几乎可以忽略不计。
在Hive 3.0中即使遇到数据倾斜,语句1将hive.optimize.countdistinct设置为true,写法也能达到语句2的效果。

表过滤

表过滤是指过滤掉同一个SQL 语句需要多次访问相同表的数据,将重复的访问操作过滤掉并压缩成只读取一次。表过滤的常见操作就是使用multi-group-by语法替换多个查询语句求并集的句式。

set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
explain
insert into table insert_table partition(tp)
select s_age, min(s_birth) as birth, 'max' as label
from user_info
group by s_age
union all 
select s_age, max(s_birth) as birth, 'min' as label
from user_info
group by s_age

上面的sql,user_info表会读两次

set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
explain
from user_info
insert into table student_stat partition(tp)
select s_age, min(s_birth) as birth, 'max' as label
group by s_age
insert into table  student_stat partition(tp)
select s_age, max(s_birth) as birth, 'min' as label
group by s_age;

参数优化

with语句

hive的with语句默认生成with语句一个视图,并不会把数据物化。使用with语句代码看起来相对简洁;如果sql中with的代码块有多次调用,会重复生成with执行计划,不一定会提高执行效率。
在高版本中,with语句可物化,参数为:hive.optimize.cte.materialize.threshold,参数默认是-1关闭。如果开启(大于0),比如n,当with…as语句被引用n次以上,会物化with生成的表,对应的with语句只执行一次。

-- 该sql中,tmp_tb1有调用了两次,该部分语句只会执行一次
set hive.optimize.cte.materialize.threshol=2
with tmp_tb1 as (
	select user_id,register_date from tb 
),tmp_tb2 as (
	select user_id from tb2 where dt = '20220918' group by userid
)
select user_id,register_date from tmp_tb1 
union all
-- tmp_tb2剔除tmp_tb1的数据
select t2.user_id,'20220918' as register_date
from tmp_tb2 t2 left join tmp_tb1 t1 
on t2.user_id=t1.user_id 
where t1.user_id is null 

向量运算

hive开启向量计算,将一次处理一条数据变为一次处理1万条数据,以此提高程序的性能
hive.vectorized.execution.enabled:表示是否开启向量模式,默认值为false
开启:set hive.vectorized.execution.enabled = true;

目前MapReduce计算引擎只支持Map端的向量化执行模式,Tez和Spark计算引擎可以支持Map和Reduce端的向量化执行模式

Hint

hive.ignore.mapjoin.Hint:是否忽略SQL中MapJoin的Hint关键,在Hive 0.11版本之后默认值为true,即开启忽略Hint的关键字。如果要使用MapJoin的Hint关键字,要在使用前开启支持Hint语法,否则达不到预期的效果。
Hint关键字,比如/*+ MAPJOIN(smalltable)*/

mapjoin

大表join小表
hive.auto.convert.join:是否开启MapJoin自动优化,hive 0.11版本以前默认关闭, 0.11及以后的版本默认开启。
hive.smalltable.filesize or hive.mapjoin.smalltable.filesize:默认值2500000(25MB)如果大小表在进行表连接时的小表数据量小于这个默认值,则自动开启MapJoin优化。在Hive 0.8.1以前使用hive.smalltable.filesize,之后的版本使用hive.mapjoin.smalltable.filesize参数。

  • hive.mapjoin.optimized.hashtable:默认值是true, Hive 0.14新增,表示使用一种内存优化的哈希表去做MapJoin。由于该类型的哈希表无法被序列化到磁盘,因此该配置只能用于Tez或者Spark。
  • hive.mapjoin.optimized.hashtable.wbsize:默认值是10485760(10MB),优化的哈希表使用的是一种链块的内存缓存,该值表示一个块的内存缓存大小。这种结构对于数据相对较大的表能够加快数据加载,但是对于数据量较小的表,将会分配多余的内存。
  • hive.vectorized.execution.mapjoin.native.enabled:是否使用原生的向量化执行模式执行MapJoin,它会比普通MapJoin速度快。默认值为False。

mapjoin失效情况
mapjoin在left或者right连接,小表为主表时会失效。
如下:小表 left join,大表开启了两个map,对于小表为2这一行,在上面这个map,由于没有2,大表为null,但在第二个map时,大表存在2这行有记录。这样就会出现一个问题,在大表某一个map不存在2这条记录时,大表的这个字段是为null还是2?
不可操作,所以该种情况mapjoin失效
HIVE优化:语句、参数、表结构优化_第1张图片

map端预聚合

Map端聚合通常指代实现Combiner类。Combiner也是处理数据聚合,不同于Reduce是聚合集群的全局数据。Combiner聚合是Map阶段处理后的数据,处理类似于spark里边的reduceByKey。
Map预聚合目标可以减少Shuffle数据量。如果数据经过聚合后不能明显减少,那就是浪费机器的I/O资源。

  • hive.map.aggr:是否开启Map任务的聚合,默认值是true。
  • hive.map.aggr.hash.min.reduction:是一个阈值,默认值是0.5。
  • hive.groupby.mapaggr.checkinterval:默认值是100000。Hive在启用Combiner时会尝试取这个配置对应的数据量进行聚合,将聚合后的数据除以聚合前的数据,如果小于hive.map.aggr.hash.min.reduction会自动关闭。
  • hive.map.aggr.hash.percentmemory:默认值是0.5。该值表示在进行Mapper端的聚合运行占用的最大内存。例如,分配给该节点的最大堆(xmx)为1024MB,那么聚合所能使用的最大Hash表内存是512MB,如果资源较为宽裕,可以适当调节这个参数。
  • hive.map.aggr.hash.force.flush.memory.threshold:默认值是0.9。该值表示当在聚合时,所占用的Hash表内存超过0.9,将触发Hash表刷写磁盘的操作。例如Hash表内存是512MB,当Hash表的数据内存超过461MB时将触发Hash表写

文件压缩

减少Shuffle数据量,开启压缩同时意味着有解压缩消耗,一般适用于大型作业。
开启文件作业的压缩只要将hive.exec.compress.intermediate参数设置为true
压缩如果要是MapReduce中起作用,前提是需要配置mapred.output.compression. codec和mapred.output.compression两个属性。

合并小文件

  • 启用hive.merge.mapfile参数,默认启用,合并只有Map任务作业的输出文件;
  • 启用hive.merge.mapredfiles参数,默认启用,合并MapReduce作业最终的输出文件;
  • 设置hive.merge.smallfiles.avgsize参数,默认16MB,当输出的文件小于该值时,启用一个MapReduce任务合并小文件;
  • 设置hive.merge.size.per.task参数,默认256MB,是每个任务合并后文件的大小。一般设置为和HDFS集群的文件块大小一致。

作业并行

hive.optimize.countdistinct:默认值为true, Hive 3.0新增的配置项。当开启该配置项时,去重并计数的作业会分成两个作业来处理这类SQL,以达到减缓SQL的数据倾斜作用。
hive.exec.parallel:默认值是False,是否开启作业的并行。默认情况下,如果一个SQL被拆分成两个阶段,如stage1、stage2,假设这两个stage没有直接的依赖关系,还是会采用窜行的方式依次执行两个阶段。如果开启该配置,则会同时执行两个阶段。在资源较为充足的情况下开启该配置可以有效节省作业的运行时间。

相关性优化

hive.optimize.correlation:默认值为false,打开该配置可以减少重复的Shuffle操作。
比如sql,join已经按product_type分区,groupby没必要重新Shuffle。

select t2.product_type,sum(t1.sale) as sale 
from dw_cus_sale_ptd t1 
join dim_product_info t2 
on t1.product_type = t2.product_type 
group by t2.product_type`

本地执行

文件数量大,使用分布式计算,多台机器并行计算可以显著减少计算时间。当hive数据量非常小,查询触发执行任务消耗的时间可能会比实际job的执行时间要多的多。这种情况,hive可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显减少。
相关参数:
set hive.exec.mode.local=true开启本地mr
set hive.exec.mode.local.auto.inputbytes.max=5000000设置local mr的最大数据数据量,当输入数据量小于这个值时采用local mr的方式,默认为134217728,即128M。
set hive.exec.mode.local.auto.input.files.max=12`设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式,默认为4。

Fetch抓取

Fetch抓取:hive中某些情况可以不必使用mr计算,例如:select * from table1。这种情况,hive可以简单读取文件输出到控制台。
在hive-default.xml.tempate文件中,hive.fetch.task.conversion默认是more,老版本是minimal,这个属性修改为more后,全局查找,字段查找,limit查找不走mr计算。
在关系型数据库中使用select * from table limit 10会全表扫描再limit。

其他

  • hive.map.groupby.sorted:在Hive 2.0以前的默认值是False,2.0及2.0以后的版本默认值为true。对于分桶或者排序表,如果分组聚合的键(列)和分桶或者排序的列一致,将会使用BucketizedHiveInputFormat。

  • hive.vectorized.execution.mapjoin.minmax.enabled:默认值为False,是否使用vector map join哈希表,用于整型连接的最大值和最小值过滤。


连接优化器

CBO(成本优化器:Cost Based Optimizer)可以基于收集到的统计信息,估算出每个表连接的组合,生成一个成本代价最低的表连接方案,预先两两结合生成中间结果集,再针对这些中间结果集进行操作。
简化表的连接,在多表连接的情况下,CBO在解析SQL子句时,会识别并抽取相同的连接谓词,并根据情况适当构造一个隐式的连接谓词作为替换,以避免高昂的表连接操作。

select tb1.id
	,tb2.field2
	,tb3.field3
	,tb4.field4
from table1 tb1 
join table2 tb2
on tb1.id = tb2.id
join table3 tb3
on tb2.id = tb3.id
join table4 tb4
on tb3.id = tb4.id

正常来说,tb3的join要等待tb1和tb2的join之后发生,这样无法充分利用集群计算。优化后的执行过程可能是:tb1 join tb2和tb3 join tb4同时进行。
step1:
tb1 join tb2 -> tb_tmp1
tb3 join tb4 -> tb_tmp2
step2:
tb_tmp1 join tb_tmp2


其他相关

查看SQL执行计划

  • 查看执行计划的基本信息,即explain;
  • 查看执行计划的扩展信息,即explain extended;
  • 查看SQL数据输入依赖的信息,即explain dependency;
  • 查看SQL操作相关权限的信息,即explain authorization;
  • 查看SQL的向量化描述信息,即explain vectorization。

show:查看信息

  • show databases:获取hive的库名列表。
  • show tables:获取当前库下的表名列表。
  • show partitions表名:获取表的分区列表。
  • show functions:获取当前可用函数列表。
  • show views:获取当前库下的视图列表,Hive 2.2版本中新增。
  • show tblproperties:获取某个表的表配置属性。
  • show create table表名:获取某个表的完整建表语句。
  • show column from表名:获取表的所有字段名。
  • show locks:获取某个库、表或者某个表的分区的锁信息。
  • show transactions:获取库内的事务信息。
  • show compactions:获取已经被压缩或正在被压缩的表/分区的压缩信息。
  • show conf ’值名’:查看配置项当前的值,如show conf ‘hive.execution.engine’。
  • show table extended like表名:获取表的大部分信息,包括数据存储位置、输入/输出格式、列基本信息和表的配置信息等
  • desc:描述库表信息

  • desc database库名,用于获取库的描述,包括库的存储位置及权限信息。
  • desc [extended|formatted]表/分区,用于获取表或者某个表的分区的详细信息,比show table extend更加具体。

load:数据加载

将本地/集群上的某个文件直接插入表/分区

LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename
[PARTITION (partcol1=val1, partcol2=val2 ...)]

export命令

将表或分区的数据连同元数据导出到指定的输出位置。

EXPORT TABLE tablename [PARTITION (part_column="value"[, ...])]
TO 'export_target_path' [ FOR replication('eventid') ]

推荐:《Hive性能调优实战》

你可能感兴趣的:(SQL,hive,大数据,hadoop,HIVE优化)