文章目录
概述
网上关于Hive调优的视频、博客等相当多,但是也很杂。本文总结记录Hive企业使用时常见的优化策略。
FetchTask→
为什么有的sql执行mapreduce,而有的却不?比如执行select * 的时候不会跑MR,这其实就是优化,属于FetchTask。
- 默认配置
在hive.default.xml.template中,搜索hive.fetch.task.conversion
<property>
<name>hive.fetch.task.conversion</name>
<value>minimal</value>
<description>
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.
1. minimal : SELECT STAR, FILTER on partition columns, LIMIT only
2. more : SELECT, FILTER, LIMIT only (TABLESAMPLE, virtual columns)
</description>
</property>
该配置的描述:某些选择查询可以转换为单个FETCH任务,从而最大程度地减少延迟。
默认参数:minimal,表示对select *、分区字段过滤、limit语法,不使用MR。改为more的话,会支持更多的FetchTask。
- 修改配置
将以上配置复制并粘贴到hive-site.xml,minimal改为more。
为大表创建子表→
- 理解
有些表的字段特别多,但是不同业务可能只会用到其中的不同字段,因此为大表创建子表能提高效率。 - 方式
子查询:将子查询的结构赋予一张新的表;用于分表。
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
# 中间是对表的设置,如:
[
[ROW FORMAT row_format]
[STORED AS file_format]
| STORED BY 'storage.handler.class.name' [WITH SERDEPROPERTIES (...)] -- (Note: Available in Hive 0.6.0 and later)
]
[LOCATION hdfs_path]
#
[AS select_statement];
外部表和分区表→
外部表
- 概念:外部表和内部表(又叫管理表或托管表)。管理表删除的时候是删除元数据和表的对应文件夹,外部表删除的时候只删除元数据。
- 使用场景:多个部门共用一个数据时,先创建管理表,然后可以创建多个外部表,因为删除外部表时,只删除对应的元数据,数据文件保留。
- 创建:创建外部表,对HDFS 已经存在的数据文件,通过location 指定文件位置。
举例:
create EXTERNAL table dept_ext( # 加EXTERBAL
deptno int,
dname string,
loc string
)
row format delimited fields terminated by '\t'
LOCATION '/user/hive/warehouse/db_emp.db/dept'; # LOCATION指定位置
- 作用:保证数据的安全性。
分区表
- 概念:分区表实际上就是对应一个HDFS文件系统上的独立的文件夹,该文件夹下是该分区所有的数据文件。Hive中的分区就是分目录,把一个大的数据集根据业务需要分割成更下的数据集。在查询时通过WHERE子句中的表达式来选择查询所需要的指定的分区,这样的查询效率会提高很多。
- 使用场景:按年、按月、按日分区,一般二级分区就够了。
- 创建:
CREATE EXTERNAL TABLE [IF NOT EXISTS] [db_name.]table_name [(col_name data_type [COMMENT col_comment], ...)] [PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)] [ROW FORMAT row_format] 举例: create table emp_part( empno int, ename string, job string, mgr int, hiredate string, sal double, comm double, deptno int ) PARTITIONED BY (event_month string,hour string) row format delimited fields terminated by '\t'; load data local inpath '/opt/datas/emp.txt' into table emp_part PARTITION(date='20191212',hour='12'); load data local inpath '/opt/datas/emp.txt' into table emp_part PARTITION(date='20191212',hour='13');
- 作用:提高查询检索的效率
注意
分区表建立完成后,用dfs -put 方法加载数据文件之后,会发现查不到数据。原因在于存储元数据的数据库里,没有元数据信息。有两种修复方式如下,企业中常用第二种。
-
第一种方式:msck repair table table_name;
-
第二种方式:alter table table_nameadd partition(分区条件);
-
举例如下:
dfs -mkdir -p /user/hive/warehouse/dept_part/day=20191212; dfs -put /opt/datas/dept.txt /user/hive/warehouse/dept_part/day=20191212; 第一种方式: hive(default)>msck repair table dept_part; 第二种方式 alter table dept_part add partition(day='20191212');
外部表和分区表使用方式:结合使用,多级分区
数据→
- 存储格式(textfile/orc/parquet)
- 数据压缩(snappy)
对于这一块,可以参照本人另一篇文章Hive数据格式
Join优化→
分类
- Common/Shuffle/Reduce Join
- Map Join
- SMB Join
Common/Shuffle/Reduce Join
定义:Join发生在Reduce Task阶段
使用场景:大表对大表,每个表的数据都是从文件中读取的。
Map Join
定义:Join发生在Map Task阶段
使用场景:小表对大表,大表的数据从文件中读取,小表的数据放在每个节点的内存中。由DistributionCache类实现。
SMB Join
- 定义:Sort-Merge-Bucket Join,SMB Join是对Reduce Join的优化。
SMB Join的流程:先对两个大表按某个字段进行排序,对于上图而言,自然应该是customer.id和order.cid;排序完之后将表分成若干个“桶”,如下示例的3 bucket,每个对应的bucket之间进行Join。
customer
3 bucket
1st
1001 - 1101
2nd
1201 - 1401
3rd
1501 - 1901
order
1st
1001 - 1101
2nd
1201 - 1401
3rd
1501 - 1901
配置
- Map Join
Hive会自动判断是“小表对大表”还是“大表对大表”,启动只需要通过sethive.auto.convert.join=true;
- SMB Join
如下图所示三个配置内容
查看hql执行计划→
EXPLAIN [EXTENDED|CBO|AST|DEPENDENCY|AUTHORIZATION|LOCKS|VECTORIZATION|ANALYZE] query
加上EXTENDED可以知道更多细节
并行执行→
- 概念
针对有些互相没有依赖关系的独立的job,可以选择并发的执行job。
如一个语句里有多个job
job1:a join b,结果:ab
job2:c join d,结果:cd
job3:ab join cd
job1和job2之间没有依赖关系,可以并行执行。
- 配置
hive.exec.parallel
-》是否开启并行执行的功能
hive.exec.parallel.thread.number
-》设置并行执行的线程个数
一般在工作中会选择去开启该功能,根据实际的集群的状况和服务器的性能合理的设置线程数目,一般在10到20之间。
JVM重用→
- JVM 开启和关闭都会占资源,也需要时间
- JVM重用即JVM 一次性运行多个任务
- 配置:
mapreduce.job.jvm.numtasks
- 通过合理的测试,设置一个合理的数目,不能过大
Map和Reduce数目→
Map
- Hadoop里Map的数目主要由块决定
- 因此也要根据实际块的大小进行设置
- 配置:hive.merge.size.per.task
- 额外一提:Hadoop源码中有一个计算公式:
min(max_split_size,max(min_split_size,block_size))
。
其中,min_split_size默认值0(最小分片大小);
max取的时候取的是block_size,block_size默认是128;
max_split_size默认值256(最大分片大小);
这个公式决定了map的个数。
一般在实际的生产环境中HDFS一旦format格式化之后,block_size大小不会去修改的。
所以不能直接去修改HDFS的block_size,我们通过修改max_split_size和min_split_size来影响map的个数
Reduce
- 配置:
mapreduce.job.reduces
- 没有固定的值,最好的经验就是拿业务进行测试。设置不同的Reduce数目,执行时间也会不同,趋势服从正太分布。
推测执行→
- 概念:当某个任务出现迟迟不结束的情况,那么会考虑开启推测执行,开启另一个任务去完成。两个任务谁先完成,就会kill掉另一个。分为map端的推测和reduce端的推测。
- 弊端:过多的消耗资源/ 额外消耗节点资源;可能会出现重复写入的情况,产生异常
- 配置:下面三个都要设置为false
mapreduce.map.speculative
mapreduce.reduce.speculative
hive.mapred.reduce.tasks.speculative.execution(这个也要设为false,曾经因为没有改,推测执行还保持存在)
动态分区调整→
- 场景演示
公司的日志源表:company_log_src
假设有三个业务,对应三个表。表类型:外部表(技术部、销售部、产品部都要用),分区表(month,day)
table1:company_log_ip 与ip相关
/month=2019-12/day=12(company_log_ip目录下可以看到二级分区)
table2:company_log_page 与用户访问的网页有关
table3:company_log_refer 与用户怎么过来的有关
关于前端数据入库的方式
* 对于src表
load data ....
* 对应company_log_xx表
load data path '' into table company_log_ip partition(month='2019-12',day='12') ;
分区目录要手动创建,对于month每月创建可以接受,但是每天都要创建day分区,会很麻烦,所以要设置动态分区。
- 配置内容
Strict Mode→
-
概念:在Strict Mode下,对分区表查询时,在where子句中没有加分区过滤的话,会报错。具体细节如下图。
生产环境下建议开启Strict Mode,因为它会检查我们写的SQL,如果出问题,可以赶紧修改。
-
备注
设置是:set hive.mapred.mode=strict;
查看是:set hive.mapred.mode;
Hive本地模式→
业务场景:处理小数据集的时候,速度会更快一些
配置:hive.exec.mode.local.auto
本地模式的限制:数据的输入大小不能超过128MB
本地模式的限制:map数不能超过4个
本地模式的限制:reduce的个数不能超过1个
数据倾斜常见问题及调优方案
正在整理中