Hive调优相关

前言

Hive是由Facebook 开源用于解决海量结构化日志的数据统计,是基于Hadoop 的一个数据仓库工具,可以将结构化的数据文件映射为一张表,并提供类 SQL查询功能。 在资源有限的情况下,提高作业的查询效率从而达到快速产出数据的想法势在必行。掌握Hive的调优方法能够提升工作效率同时提高任务执行的稳定性。本文会从以下几个方面介绍Hive调优的思路:

1.设计优化

分区表和索引

对表进行合理的管理以及提高查询效率,分区是表的部分列的集合,可以为频繁使用的数据建立分区,这样查找分区中的数据时就不需要扫描全表,这对于提高查找效率很有帮助。Hive中每个分区对应着表很多的子目录,将所有的数据按照分区列放入到不同的子目录中去。Hive的分区使用HDFS的子目录功能实现。每一个子目录包含了分区对应的列名和每一列的值。但是由于HDFS并不支持大量的子目录,这也给分区的使用带来了限制。我们有必要对表中的分区数量进行预估,从而避免因为分区数量过大带来一系列问题。分区表本质上就是对应一个HDFS文件系统上独立的文件夹,该文件夹下对应的是该分区的所有数据文件。

分区字段主要有以下内容

  • 从时间维度:year,month,week,day,hour等
  • 从业务逻辑:客户,销售区域或部门等等
  • 从地理位置:国家、省份和城市区域等
#创建分区表
create table dept_partition(
deptno int, dname string, loc string
)
partitioned by (month string)
row format delimited fields terminated by '\t';
#加载数据到分区表
load data local inpath
'/opt/depts.txt' into table default.dept_partition
partition(month='201909');
#查询分区表
select * from dept_partition where month='201909';

Hive支持索引,但是Hive的索引与关系型数据库中的索引并不相同,比如,Hive不支持主键或者外键。若未建立索引,每次查询都会加载整张表并处理所有的rows,若建立索引便只会拆线呢和处理文件的一部分

#创建索引
CREATE INDEX idx_user_id
ON TABLE user_behavior (user_id)
AS 'COMPACT'
WITH DEFERRED REBUILD;
#基表变动后状态更新
ALTER INDEX idx_user_id ON user_id REBUILD;
#查看索引表结构
DESC default__user_behavior_idx_user_id_user_behavior__;
OK
user_id int
_offsets array<bigint>
Time taken: 0.089 seconds, Fetched: 2 row(s)
#删除索引
DROP INDEX idx_user_idr ON user_id;

表相关优化

  • 小表Join大表

    我们熟知的小表Join大表左右位置的问题已经在新版的Hive进行了优化,所以小表在左边还是右边已经没有没有明显区别。

  • 大表Join大表

    在join时,相同的key对应的数据都会发送到相同reduce上,可能导致内存不足,这时就有必要分析异常key。空key通常时导致这种情况的主要原因,可以配置Hadoop历史服务器查看Jobhistory。在有原始数据表的情况下创建空值表以及合并后数据表(三张表字段一致),将空值数据加载到对应表中Join过滤空值。

  • MapJoin

开启MapJoin可有效防止Hive解析器将Join操作转换成Common Join,此阶段会在reduce完成join极大概率导致数据倾斜。

SET hive.auto.convert.join = true; 默认为true
SET hive.mapjoin.smalltable.filesize=70000000; 小表阈值,默认25M以下都是小表
SET hive.auto.convert.join.noconditionaltask=true; -- 默认true,所以不需要指定map join hint
SET hive.auto.convert.join.noconditionaltask.size=10000000; -- 控制加载到内存的表的大小
  • Group By

    默认情况下,Map 阶段同一 Key 数据分发给一个 reduce,当一个 key 数据过大时就倾

    斜了。 并不是所有的聚合操作都需要在 Reduce端完成,很多聚合操作都可以先在 Map端进行

    部分聚合,最后在Reduce端得出最终结果。
hive.map.aggr = true
hive.groupby.mapaggr.checkinterval = 100000 在Map 端进行聚合操作的条目数目
hive.groupby.skewindata = true 有数据倾斜的时候进行负载均衡(默认是false)
  • Count(Distinct)去重统计

    在海量数据的情况下这两项操作会单独开启一个reduce task来完成,会导致单个reduce数据量过大导致整个job很难完成,一般使用Group By 再Count的方式替换

存储优化

压缩

在MR任务执行过程中,有相当大一部分时间是在磁盘和网络IO中消耗的。使用压缩技术可以使同等数据量体积缩小,减少吞吐开销从而达到提升查询性能的目的。压缩指令可以在单次的任务CLI开启或在hive-site.xml中配置属性

#开启hive中间传输数据压缩功能
SET hive.exec.compress.intermediate=true
#开启map输出压缩功能
SET mapreduce.map.output.compress=true;
#设置map输出数据的压缩方式
SET mapreduce.map.output.compress.codec=
org.apache.hadoop.io.compress.SnappyCodec;
#输出结果压缩
SET hive.exec.compress.output=true;
SET mapreduce.output.fileoutputformat.compress.codec=
org.apache.hadoop.io.compress.SnappyCodc

压缩指令开启之后,可以选择下面的压缩格式

Hive调优相关

文件格式

Hive调优相关

hive支持多种包括行和列形式的存储格式

其中TEXTFILE(默认文件格式)和SEQUENCEFILE(k-v二级存储格式)以及AVRO都是面向行的文件存储格式,对于海量数据的任务查询来说有短板,因为即使只查询一列也需要读取完整的一行数据

文件格式 特点
TEXTFILE 默认,存储文本格式。可以并行处理和分割,可以使用LZO、GZip或Snappy压缩
SEQUENCEFILE k-v二进制存储格式,比文本格式更好压缩,可以被压缩成块级别
AVRO 二进制格式文件
RCFILE 将表分成若干行组,对组内数据按列存储
ORC RCFILE的升级版,在Spark SQL和Presto等各种查询引擎中都有支持
PARQUET 比ORC更广的生态支持,高效的压缩比(1TB数据压缩后仅231.4GB)和查询速度(比TEXT快30倍)

作业优化

JVM重用

严格来讲,重用这一块是属于Hadoop的调优参数,对很难避免的小文件和多task场景,大多执行时间并不长。

hadoop会为一个map和reduce派生一个JVM用以执行任务,对于上述情况,启动进程的时间开销会比作业执行的开销还大,JVM的重用可以是JVM在同一个job中使用N次。其中N就是一个作业中最大task数量,默认为1,-1表示不限制,通常生产环境在10-20之间

#cli命令
SET mapreduce.job.jvm.numtasks=12; #mapred-site.xml配置
<property>
<name>mapreduce.job.jvm.numtasks</name>
<value>12</value>
<description>How many tasks to run per jvm. If set to -1, there is
no limit.
</description>
</property>

值得注意的是,JVM重用开启后会一直占用task,直到整个任务完成才会释放,期间每个task的运行状态可能严重失衡,所以要根据业务作业实际情况设置合理的task均衡task的状态。

本地模式

除了常见的夜间定时作业情况以外,偶尔还会有输入数据量较小的情况,此时如果继续使用所有分布式节点跑作业会极大占用、浪费算力。那么在这种情况下就可以通过本地模式进行作业处理,有效缩短执行时间。

SET hive.exec.mode.local.auto=true; -- 默认 false
SET hive.exec.mode.local.auto.inputbytes.max=50000000;
SET hive.exec.mode.local.auto.input.files.max=5; -- 默认 4

执行引擎

主要引擎选择就是Tez和spark

SET hive.execution.engine=<engine>; -- <engine> = mr|tez|spark

spark:使用DAG调度分配在内存执行任务大幅增加作业的执行效率

tez:将多项作业合并为一个作业,只需对HDFS进行一次操作,以减少IO的方式提升作业计算性能 

Fetch抓取

通过配置控制Hive在某些拆线呢情况下不使用MR计算

<property>
<name>hive.fetch.task.conversion</name>
<value>more</value>
<description>
Expects one of [none, minimal, more].
Some select queries can be converted to single FETCH task
minimizing latency.
Currently the query should be single sourced not having any
subquery and should not have any aggregations or distincts (which incurs RS), lateral views
and joins.
0. none : disable hive.fetch.task.conversion
1. minimal : SELECT STAR, FILTER on partition columns, LIMIT
only
2. more : SELECT, FILTER, LIMIT only (support TABLESAMPLE and
virtual columns)
</description>
</property>

在hive-default.xml.template 文件中 hive.fetch.task.conversion 默认是 more,老版本 hive

默认是 minimal,该属性修改为 more 以后,在全局查找、字段查找、limit 查找等都不走mapreduce。

并行执行

在节点算力资源比较充足的情况下可以开启并行执行,将MR阶段、抽样阶段、合并阶段、limit阶段从默认串行改为某些阶段并行执行,使作业执行时间缩短。

set hive.exec.parallel=true;              //打开任务并行执行
set hive.exec.parallel.thread.number=12; //同一个 sql 允许最大并行度,默认为8。

推测执行

在分布式集群环境下,因为程序 Bug(包括 Hadoop 本身的 bug),负载不均衡或者资

源分布不均等原因,会造成同一个作业的多个任务之间运行速度不一致,有些任务的运行速

度可能明显慢于其他任务(比如一个作业的某个任务进度只有 50%,而其他所有任务已经

运行完毕),则这些任务会拖慢作业的整体执行进度。为了避免这种情况发生,Hadoop 采

用了推测执行(Speculative Execution)机制,它根据一定的法则推测出“拖后腿”的任务,并

为这样的任务启动一个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最

先成功运行完成任务的计算结果作为最终结果。

#hive自身的reduce-site推测执行
<property>
<name>hive.mapred.reduce.tasks.speculative.execution</name>
<value>true</value>
<description>Whether speculative execution for reducers should be turned on. </description>
</property> #mapred-site.xml中的推测执行
<property>
<name>mapreduce.map.speculative</name>
<value>true</value>
<description>If true, then multiple instances of some map tasks may be executed in parallel.</description>
</property> <property>
<name>mapreduce.reduce.speculative</name>
<value>true</value>
<description>If true, then multiple instances of some reduce tasks may be executed in parallel.</description>
</property>

关于调优这些推测执行变量,还很难给一个具体的建议。如果用户对于运行时的偏差非

常敏感的话,那么可以将这些功能关闭掉。如果用户因为输入数据量很大而需要执行长时间

的map或者Reduce task的话,那么启动推测执行造成的浪费是非常巨大大。

上一篇:hive 调优手段


下一篇:剑指Offer——小米+小红书笔试题+知识点总结