分区和分桶
- Hive中的分区就是把一张大表的数据按照业务需要分散的存储到多个目录,每个目录就称为该表的一个分区。在查询时通过where子句中的表达式选择查询所需要的分区,这样的查询效率会提高很多。(防止全表扫描)
分区表
分区表语法
创建分区表
sql
复制代码
create table dept_partition(
deptno int, -- 部门编号
dname string, -- 部门名称
loc string -- 部门位置
)
partition by (day string)
row format delimited fields terminated by '\t';
分区表读写数据
sql
复制代码
-- 数据装载
load data local inpath '/path/dept_1.log' into table dept_partition partition(day='1')
-- insert,分区字段可以用作表字段, 但在hdfs中,文件按照分区字段进行存储,存储的文件中不包含分区字段
insert overwrite table dept_partition partition (day = '1')
select deptno, dname, loc
from dept_partition where day = '1';
- 查询分区表数据时,可以将分区字段看作表的伪列,可像使用其他字段一样使用分区字段。
sql
复制代码
select deptno, dname, loc
from dept_partition where day = '1';
分区表基本操作
sql
复制代码
show partitions dept_partition;
sql
复制代码
alter table dept_partition add partition(day = '4');
sql
复制代码
alter table dept_partition
add partition(day = '4') partition(day='5');
- 删除分区(一般删除元数据信息也删除hdfs中数据,外部表只删除元数据信息,不删除hdfs中文件信息)
sql
复制代码
alter table dept_partition
drop partition(day = '1'), partition(day = '2');
修复分区(让元数据分区信息与hdfs分区数据保持一致, 依据hdfs中的文件信息修复mysql中元数据信息)
- Hive将分区表的所有分区信息都保存在了元数据中,只有元数据与HDFS上的分区路径一致时,分区表才能正常读写数据。若用户手动创建/删除分区路径,Hive都是感知不到的,这样就会导致Hive的元数据和HDFS的分区路径不一致。再比如,若分区表为外部表,用户执行drop partition命令后,分区元数据会被删除,而HDFS的分区路径不会被删除,同样会导致Hive的元数据和HDFS的分区路径不一致。
- 若出现元数据和HDFS路径不一致的情况,可通过如下几种手段进行修复。
sql
复制代码
alter table dept_partition add partition (day = '1')
sql
复制代码
alter table dept_partition drop partition (day = '1')
sql
复制代码
-- add 增加分区,根据hdfs中分区文件自动增加元数据表中的分区信息
-- del 增加分区,根据hdfs中分区文件自动删除元数据表中的分区信息
-- sync 同步分区,根据hdfs中分区文件自动增加或删除元数据表中的分区信息 相当于执行 add 与 drop命令
-- msck repair table table_name 等价于 msck repair table table_name add partitions
msck repair table table_name [add/drop/sync partitions];
二级分区
sql
复制代码
create table dept_partition2(
deptno int, -- 部门编号
dname string, -- 部门名称
loc string -- 部门位置
)
partition by (day string, hour string)
row format delimited fields terminated by '\t';
sql
复制代码
load data local inpath '/path/dept_1.log'
into table dept_partition
where day='1' and hour = '12'
sql
复制代码
select *
from dept_partition2
where day = '1' and hour = '12'
动态分区
- 动态分区是指向分区表insert数据时,被写往的分区不由用户指定,而是由每行数据的最后一个字段的值来动态的决定。使用动态分区,可只用一个insert语句将数据写入多个分区。
动态分区相关参数
sql
复制代码
set hive.exec.dynamic.partition=true
- 动态分区的模式,默认strict(严格模式),要求必须指定至少一个分区为静态分区,nonstrict(非严格模式)允许所有的分区字段都使用动态分区。
sql
复制代码
set hive.exec.dynamic.partition.mode=nonstrict
- 一条insert语句可以同时创建的最大分区个数, 默认为1000
sql
复制代码
set hive.exec.max.dynamic.partitions=1000
- 单个Mapper或者Reducer可同时创建的最大分区的个数, 默认为100
sql
复制代码
set hive.exec.max.dynamic.partitions.pernode=100
- 一条insert语句可以创建的最大文件个数,默认100000
sql
复制代码
hive.exec.max.created.files=100000
- 当查询结果为空时且进行动态分区时,是否抛出异常,默认false
sql
复制代码
hive.error.on.empty.partition=false
分桶表
- 分桶表的基本原理是,首先为每行数据计算一个指定字段的数据的hash值,然后模以一个指定的分桶数,最后将取模运算结果相同的行,写入同一个文件中,这个文件就称为一个分桶(bucket)。
基本语法
sql
复制代码
create table stu_buck(
id int,
name string
)
cluster by(id)
into 4 buckets
row format delimited fields terminated by '\t';
-- 创建分桶排序表
create table stu_buck_sort(
id int,
name string
)
clustered by(id) sorted by(id)
into 4 buckets
row format delimited fields terminated by '\t';
- 说明:Hive新版本load数据可以直接跑MapReduce,老版的Hive需要将数据传到一张表里,再通过查询的方式导入到分桶表里面。
sql
复制代码
laod data local inpath '/path/student.txt' into table stu_buck;
文件格式和压缩
Hive文件格式
- 常用文件格式有:text file 、 orc 、parquet 、sequence file等
Text File
- 文本文件是Hive默认使用的文件格式,文本文件中的一行内容,就对应Hive表中的一行记录。
sql
复制代码
create table textfile_table(
column specs
)
stored as textfile;
ORC
建表语句
sql
复制代码
create table orc_table (
column_speces
)
stored as orc
tblproperties (property_name=property_true, ... )
- 若一个文本文件想要往orc文件中load数据,直接load后在读取数据时会出错,因此需要创建一个临时表,将文本文件内容读出来之后,插入orc表中
参数 |
默认值 |
说明 |
orc.compress |
ZLIB |
压缩格式,可选项:NONE、ZLIB,、SNAPPY |
orc.compress.size |
262,144 |
每个压缩块的大小(ORC文件是分块压缩的) |
orc.stripe.size |
67,108,864 |
每个stripe的大小 |
orc.row.index.stride |
10,000 |
索引步长(每隔多少行数据建一条索引) |
parquet
建表语句
sql
复制代码
create table parquet_table(
column_specs
)
stored as parquet
tblproperties(property_name=property_value, ...);
支持参数
参数 |
默认值 |
说明 |
parquet.compression |
uncompressed |
压缩格式,可选项:uncompressed,snappy,gzip,lzo,brotli,lz4 |
parquet.block.size |
134217728 |
行组大小,通常与HDFS块大小保持一致 |
parquet.page.size |
1048576 |
页大小 |
压缩
Hive表数据进行压缩
TextFile
- Text通常无需声明,在读取文件时,会自动解压缩文件
- 使用SQL导入数据时,需设置以下参数,来保证表中数据是被压缩的。
sql
复制代码
-- SQL语句的最终输出结果是否压缩
set hive.exec.compress.output=true;
-- 输出结果的压缩格式(以下示例为Snappy压缩)
set mapreduce.output.fileoutputformat.compress.codec =org.apache.hadoop.io.compress.SnappyCodec;
ORC
- 若一张表文件类型为ORC,若需要对该表进行数据压缩,需在建表语句中声明压缩格式如下:
sql
复制代码
create table orc_table(
column_specs
)
stored as orc
tblproperties ("orc.compress"="snappy")
Parquet
- 若一张表文件类型为ORC,若需要对该表进行数据压缩,需在建表语句中声明压缩格式如下:
sql
复制代码
create table orc_table(
column_specs
)
stored as parquet
tblproperties ("parquet.compression"="snappy")
计算过程使用压缩
sql
复制代码
--开启MapReduce中间数据压缩功能
set mapreduce.map.output.compress=true;
--设置MapReduce中间数据数据的压缩方式(以下示例为snappy)
set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
- 单条SQL语句的中间结果进行压缩 (两个MR之间进行压缩)
sql
复制代码
--是否对两个MR之间的临时数据进行压缩
set hive.exec.compress.intermediate=true;
--压缩格式(以下示例为snappy)
set hive.intermediate.compression.codec=** **org.apache.hadoop.io.compress.SnappyCodec;
企业级调优
Explain执行计划
概述
- Explain呈现的执行计划,由一系列Stage组成,这一系列Stage具有依赖关系,每个Stage对应一个MapReduce Job,或者一个文件系统操作等。
- 若某个Stage对应的一个MapReduce Job,其Map端和Reduce端的计算逻辑分别由Map Operator Tree和Reduce Operator Tree进行描述,Operator Tree由一系列的Operator组成,一个Operator代表在Map或Reduce阶段的一个单一的逻辑操作,例如TableScan Operator,Select Operator,Join Operator等。
- 常见Operator及其作用如下
- TableScan:表扫描操作,通常map端第一个操作肯定是表扫描操作
- Select Operator:选取操作
- Group By Operator:分组聚合操作
- Reduce Output Operator:输出到 reduce 操作
- Filter Operator:过滤操作
- Join Operator:join 操作
- File Output Operator:文件输出操作
- Fetch Operator 客户端获取数据操作
基本语法
sql
复制代码
explain [fromatted | extended | dependency] query-sql
-- foramtted: 将执行计划以json字符串的形式输出
-- extended: 输出执行计划中的额外信息,通常是读写的文件名等信息
-- dependency: 输出执行计划读取的表及分区数据
HQL语法优化之分组聚合
- Hive中未经优化的分组聚合,是通过一个MapReduce Job实现的。Map端负责读取数据,并按照分组字段分区,通过Shuffle,将数据发往Reduce端,各组数据在Reduce端完成最终的聚合运算。
- Hive对分组聚合的优化主要围绕着减少Shuffle数据量进行,具体做法是map-side聚合。就是在map端维护一个hash-table,利用其完成部分的聚合,然后将部分聚合的结果按照分组字段进行分区,发送至reduce端,完成最终的聚合。map-side聚合能有效减少shuffle的数据量,提高分组聚合的效率。
sql
复制代码
-- 启用map-side聚合
set hive.map.aggr=true
-- 用于检测元数据表是否适合进行map-side聚合,检测方法是:先对若干条数据进行map-side聚合,若聚合后的
-- 条数和聚合前的条数的比值小于该值,则认为该表适合进行map-side聚合,否则,认为该表数据不适合进行
-- map-side聚合,后续数据便不再进行map-side聚合。
set hive.map.aggr.hash.min.reduction=0.5
-- 用于检测源表是否适合map-side聚合的条数
set hive.groupby.mapaggr.checkinterval=100000;
-- map-side聚合所用的hash table, 占用map task堆内存的最大比例,若超出该值,则会对hash table进行一
-- 次flush
set hive.map.aggr.hash.force.flush.memory.threshold=0.9
HQL语法优化之Join优化
Join算法概述
- Hive拥有多种join算法,包括Common Join,Map Join,Bucket Map Join,Sort Merge Buckt Map Join等
Common Join
- Common Join是Hive中最稳定的join算法,其通过一个MapReduce Job完成一个join操作。Map端负责读取join操作所需表的数据,并按照关联字段进行分区,通过Shuffle,将其发送到Reduce端,相同key的数据在Reduce端完成最终的Join操作。
- 需要注意 的是,sql语句中的join操作和执行计划中的Common Join任务并非一对一的关系 ,一个sql语句中的相邻 的且关联字段相同的多个join操作可以合并为一个Common Join任务。
- sql语句中的两个join操作关联字段各不相同,则该语句的两个join操作需要各自通过一个Common Join任务实现,也就是通过两个Map Reduce任务实现。
Map Join (没有reduce、shuffle)
- Map Join算法可以通过两个只有map阶段的Job完成一个join操作。其适用场景为大表join小表。若某join操作满足要求,则第一个Job会读取小表数据,将其制作为hash table,并上传至Hadoop分布式缓存(本质上是上传至HDFS)。第二个Job会先从分布式缓存中读取小表数据,并缓存在Map Task的内存中,然后扫描大表数据,这样在map端即可完成关联操作。
Bucket Map Join
- Bucket Map Join是对Map Join算法的改进,其打破了Map Join只适用于大表join小表的限制,可用于大表join大表的场景。
- Bucket Map Join的核心思想是:若能保证参与join的表均为分桶表,且关联字段为分桶字段,且其中一张表的分桶数量是另外一张表分桶数量的整数倍,就能保证参与join的两张表的分桶之间具有明确的关联关系,所以就可以在两表的分桶间进行Map Join操作了。这样一来,第二个Job的Map端就无需再缓存小表的全表数据了,而只需缓存其所需的分桶即可。
Sort Merge Bucket Map Join
- Sort Merge Bucket Map Join(简称SMB Map Join)基于Bucket Map Join。SMB Map Join要求,参与join的表均为分桶表,且需保证分桶内的数据是有序的,且分桶字段、排序字段和关联字段为相同字段,且其中一张表的分桶数量是另外一张表分桶数量的整数倍。
- SMB Map Join同Bucket Join一样,同样是利用两表各分桶之间的关联关系,在分桶之间进行join操作,不同的是,分桶之间的join操作的实现原理。Bucket Map Join,两个分桶之间的join实现原理为Hash Join算法;而SMB Map Join,两个分桶之间的join实现原理为Sort Merge Join算法。
- Hash Join和Sort Merge Join均为关系型数据库中常见的Join实现算法。Hash Join的原理相对简单,就是对参与join的一张表构建hash table,然后扫描另外一张表,然后进行逐行匹配。Sort Merge Join需要在两张按照关联字段排好序的表中进行.
- Hive中的SMB Map Join就是对两个分桶的数据按照上述思路进行Join操作。可以看出,SMB Map Join与Bucket Map Join相比,在进行Join操作时,Map端是无需对整个Bucket构建hash table,也无需在Map端缓存整个Bucket数据的,每个Mapper只需按顺序逐个key读取两个分桶的数据进行join即可。
MapJoin
Hint 提示
sql
复制代码
-- 已过时,不建议使用
select /*+ mapjoin(ta) */
ta.id,
tb.id
from table_a ta
join table_b tb
on ta.id=tb.id;
自动触发
- Hive在编译SQL语句阶段,起初所有的join操作均采用Common Join算法实现。
- 之后在物理优化阶段,Hive会根据每个Common Join任务所需表的大小判断该Common Join任务是否能够转换为Map Join任务,若满足要求,便将Common Join任务自动转换为Map Join任务。
- 但有些Common Join任务所需的表大小,在SQL的编译阶段是未知的(例如对子查询进行join操作),所以这种Common Join任务是否能转换成Map Join任务在编译阶是无法确定的。
- 针对这种情况,Hive会在编译阶段生成一个条件任务(Conditional Task),其下会包含一个计划列表,计划列表中包含转换后的Map Join任务以及原有的Common Join任务。最终具体采用哪个计划,是在运行时决定的。
sql
复制代码
--启动Map Join自动转换
set hive.auto.convert.join=true;
-- 一个Common Join operator转为Map Join operator的判断条件,若该Common Join相关的表中,存在n-1张表的
-- 已知大小总和<=该值,则生成一个Map Join计划,此时可能存在多种n-1张表的组合均满足该条件,则hive会为每
-- 种满足条件的组合均生成一个Map Join计划,同时还会保留原有的Common Join计划作为后备(back up)计划,实
-- 际运行时,优先执行Map Join计划,若不能执行成功,则启动Common Join后备计划。
set hive.mapjoin.smalltable.filesize=250000;
--开启无条件转Map Join
set hive.auto.convert.join.noconditionaltask=true;
--无条件转Map Join时的小表之和阈值,若一个Common Join operator相关的表中,存在n-1张表的大小总和<=该
-- 值,此时hive便不会再为每种n-1张表的组合均生成Map Join计划,同时也不会保留Common Join作为后备计划。
-- 而是只生成一个最优的Map Join计划。
set hive.auto.convert.join.noconditionaltask.size=10000000;
sql
复制代码
sesc formatted table_name partition(partition_col='partition');
Bucker Map Join
Hint提示
sql
复制代码
select /*+ mapjoin(ta) */
ta.id,
tb.id
from table_a ta
join table_b tb on ta.id = tb.id
相关参数
sql
复制代码
-- 关闭cbo优化,cbo会导致Hint信息被忽略,需将如下参数修改为false
set hive.cbo.enable=false;
-- map join hint默认会被忽略(因为已经过时),需将如下参数修改为false
set hive.ignore.mapjoin.hint=false;
-- 启用bucket map join 优化功能,默认不启用,需将如下参数修改为true
set hive.optimize.bucketmapjoin = true
Sort Merge Bucket Map Join
sql
复制代码
-- 启动Sort Merge Bucker Map Join
set hive.optimize.bucketmapjoin.sortedmerge=true;
-- 使用自动转换SMB Join
set hive.auto.convert.sortmerge.join=true;
HQL语法优化之数据倾斜
- 数据倾斜问题通常指的是参与计算的数据分布不均匀,即某个key或者某些key的数据量远超其他key,导致在shuffle阶段, 大量相同的kay的数据被发往同一个Reduce,进而导致该Reduce所需的时间远远超过其他Reduce, 成为整个任务的瓶颈。
- Hive中数据倾斜通常出现在分组聚合和join操作的场景中。
分组聚合导致的数据倾斜
- Hive的MapReduce Job中, Map 端负责读取数据,并按照分组字段进行分区,通过Shuffle,将数据发往Reduce端,各组数据在Reduce端完成最终的聚合运算。
- 如果group by分组字段的值分布不均匀,就有可能导致大量相同的key进入同一Reduce,从而导致数据倾斜问题。
- 由分组聚合导致的数据倾斜问题,有以下两种解决思路。
Map-side聚合
- 开启Map-Side聚合后,数据会先在Map端完成部分聚合工作,这样一来即便原始数据是倾斜的,经过Map端的初步聚合后,发往Reduce的数据也就不再倾斜了。最佳状态下,Map-端聚合完全能够屏蔽数据倾斜问题。
sql
复制代码
--启用map-side聚合
set hive.map.aggr=true;
-- 用于检测源表数据是否适合进行map-side聚合。检测的方法是:先对若干条数据进行map-side聚合,若聚合后的
-- 条数和聚合前的条数比值小于该值,则认为该表适合进行map-side聚合;否则,认为该表数据不适合进行map-
-- side聚合,后续数据便不再进行map-side聚合。
set hive.map.aggr.hash.min.reduction=0.5;
-- 用于检测源表是否适合map-side聚合的条数。
set hive.groupby.mapaggr.checkinterval=100000;
-- map-side聚合所用的hash table,占用map task堆内存的最大比例,若超出该值,则会对hash table进行一次
-- flush
set hive.map.aggr.hash.force.flush.memory.threshold=0.9;
Skew-GroupBY优化
- Skew-GroupBy的原理是启动两个MR任务,第一个MR按照随机数进行分区,将数据发散发送到Reduce,完成部分聚合,第二个MR按照分组字段分区,完成最终聚合。
- 一般而言Map-Side聚合效率更高,因为其只需要一个MR任务,且在Map端内存缓冲数据进行聚合。但也存在极端情况,如当前Container内存较小,由于在Map端需要缓存一个Hash Table,因此会发生多次flush, 可能导致输入到Reduce端的数据依然会发生倾斜。
sql
复制代码
--启用分组聚合数据倾斜优化
set hive.groupby.skewindata=true;
Join导致的数据倾斜
- 未经优化的join操作,默认使用的是common join算法,也就是通过一个Mapreduce Job完成计算。Map端负责读取join操作所需表的数据,并按照关联字段进行分区,通过Shuffle,将其发送到Reduce端,相同的Key的数据在Reduce端完成最终的Join操作。
- 如果关联字段的值分布不均匀,就可能导致大量相同的key进入同一Reduce,从而导致数据倾斜问题。
Map Join
- 使用Map Join算法,join操作仅在map端就能完成,没有shuffle操作,没有reduce阶段,自然不会产生reduce端的数据倾斜。该方案适用于大表join小表时发生数据倾斜的场景。
sql
复制代码
-- 启动Map join自动转换
set hive.auto.convert.join=true;
-- 一个Common Join operator转换未Map Join operator的判断条件,若该Common Joini相关的表中,存在
-- n - 1张表的大小总和 <= 该值,则生成一个Map Join计划, 此时可能存在多种n - 1张表的组合均满足条件
-- 则hive会为每种满足条件的组合均生成一个Map Join计划, 同时还会保留原有的Common Join计划作为后备,
-- 实际运行时, 优先执行Map Join计划,若不能成功,则启动Common Join后备计划
set hive.mapjoin.smalltable.filesize=250000;
-- 开启无条件转Map Join
set hive.auto.convert.join.noconditionaltask=true;
-- 无条件转Map Join时的小表之和阈值, 若一个Common Join Operator 相关的表中,存在n - 1张表的大小总
-- 和 <= 该值, 此时hive便不会再为每种n - 1张表的组合均生成Map Join计划,同时也不会保留Common Join
-- 作为后备计划。而只生成一个最优的Map Join计划。
set hive.auto.convert.join.noconditionaltask.size=10000000;
skew Join, 适用于大表join 大表
- skew join的原理是,为倾斜的大key单独启动一个Map Join任务进行计算,其余key进行正常的common join。
- 这种方案对参与join的源表大小没有要求,但对两表中倾斜的key的数据量有要求,要求一张表中的倾斜的key的数据量比较小。(方便走map join, 在第二个MR的map端缓存hash table)
sql
复制代码
-- 启用skew join优化
set hive.optimize.skewjoin=true;
-- 触发skew join的阈值,若某个key的函数超过该参数值,则触发
set hive.skewjoin.key=100000;
调整SQL语句
- 若参与Join的两表均为大表,其中一张表的数据是倾斜的,此时也可通过以下方式对SQL语句进行相应的调整
sql
复制代码
select
*
from A
join B
on A.id = B.id;
- 能够join上的数据被发往同一个Reduce,可能造成数据倾斜,可通过以下代码优化
sql
复制代码
select
*
from (
select -- 打散操作
concat(id, '_', cast(rand() * 2 as int)) id,
value
from A
) ta
join (
select -- 扩容操作
concat(id, '_', 0) id,
value
from B
union all
select
concat(id, '_', 1) id,
value
from B
) tb
on ta.id = tb.id;
HQL语法优化之任务并行度
Map端并行度
- Map端并行度即Map的个数。是由输入文件的切片数量决定的。一般情况下,Map端的并行度无需调整,但会存在以下特殊情况。
查询表中存在大量小文件
- 按照Hadoop中的文件处理策略,一个小文件会单独启动一个map task负责计算。若查询的表中存在大量小文件,则会启动大量map task,造成资源浪费。这种情况下,可以使用Hive提供的CombinHiveInputFormat,多个小文件合并成一个切片,从而控制map task个数。
sql
复制代码
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
map端有复杂的查询逻辑
- sql语句中where之后的条件过滤在map端进行(filter operator),若sql中有正则替换、json解析等复杂耗时的查询逻辑时,map端的计算会相对慢一些。若想加快计算速度,在计算资源充足的情况下,可考虑增大map端并行度,令map task多一些,每个map task计算的数据少一些。
sql
复制代码
-- 一个切片的最大值
set mapreduce.input.fileinputformat.split.maxsize=256000000;
Reduce端并行度
- Reduce端的并行度,也就是Reduce的个数。相对来说更需要关注。Reduce端的并行度,可以由用户自己指定,也可以由Hive根据该MR Job输入的文件大小进行估算。 Reduce端的并行度的相关参数如下:
sql
复制代码
-- 指定Reduce端并行度,默认值为-1,表示用户未指定
set mapreduce.job.reduces;
-- 指定Reduce端并行度的最大值
set hive.exec.reducers.max
-- 单个Reduce Task计算的数据量,用于估算Reduce并行度
set hive.exec.reducers.bytes.per.reducer;
- Reduce端并行度的确认逻辑如下:
- 若指定参数mapreduce.job.reduces的值为一个非负整数,则Reduce并行度为指定值。否则,Hive自行估算Reduce并行度,估算逻辑如下:
- 假设Job输入的文件大小为totalInputBytes
- 参数hive.exec.reducers.bytes.per.reducer的值为bytesPerReducer
- 参数hive.exec.reducers.max的值为maxReducers
- 则Reduce端的并行度为:min(ceil(totalInputBytes / bytesPerReducer), maxReducers)
- 根据以上描述,可以看出Hive自行估算Reduce并行度时,是以整个MR Job输入的文件大小作为依据的。因此,在某些情况下其估计的并行化度可能并不准确,此时就需要用户根据实际情况来指定Reduce并行度。
HQL语法优化之小文件合并
Map端驶入文件合并
- 合并Map端输入的小文件,是指将多个小文件划分到一个切片中,进而由一个Map Task处理。目的是为了防止单个小文件启动一个MapTask,浪费计算资源。
sql
复制代码
-- 可将多个小文件切片,合并为一个切片,进而由一个map任务处理
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
Reduce输出文件合并
- 合并Reduce端输出的小文件,是指将多个小文件合并成大文件,目的是减少HDFS小文件数量。其原理是根据计算任务输出文件的平均大小进行判断,若符合条件,则单独启动一个额外的任务进行合并。
sql
复制代码
-- 开启合并map only任务输出的小文件(默认false)
set hive.merge.mapfiles=true;
-- 开启合并map reduce任务输出的小文件(默认false)
set hive.merge.mapredfiles=true;
-- 合并后的文件大小
set hive.merge.size.per.task=256000000;
-- 触发小文件合并任务的阈值,若某计算任务输出的文件平均大小低于该值,则触发合并
set hive.merge.smallfiles.avgsize=16000000;
其他优化
CBO优化
- CBO是指Cost based Optimizer, 即基于计算成本的优化
- 在Hive中,计算成本模型考虑到了:数据的行数、CPU、本地IO、HDFS IO、网络IO等方面。Hive会计算同一SQL语句的不同执行计划的计算成本,并选择出成本最低的执行计划。目前在hive的MR引擎下主要用于join优化,例如多表join的join顺序。(join 的中间结果数据量越小,整个计算任务的数据量就会越小,计算成本越小,执行速度越快)。
sql
复制代码
-- 是否启用cbo优化
set hive.cbo.enable=true;
谓词下推(通常指where过滤)
- 谓词下推(predicate pushdown)是指,尽量将过滤操作前移,以减少后续计算步骤的数据量。
sql
复制代码
-- 是否启动谓词下推优化
set hive.optimize.ppd=true;
- CBO优化也会完成一部分谓词下推优化,因为在执行计划中,谓词越靠前,整个计划的计算成本也就会越低。
矢量化查询
- 计算方式矢量化
- Hive的矢量化查询,可以极大提高一些典型查询场景(例如scans, filters, aggregates, and joins)下的CPU使用效率。
sql
复制代码
set hive.vectorized.execution.enabled=true;
-- 若执行计划中,出现"Execution mode: vectorized"字样,即表明使用了矢量化计算。
官网参考链接
Fetch 抓取
- 针对Hive中的某些查询可以不必使用MapReduce计算。 如 select * from emp; 这种情况下,Hive可以简单读取emp对应的存储目录下的文件,然后输出查询结果到控制台。
sql
复制代码
-- 是否在特定场景转换为fetch 任务
-- 设置为none表示不转换
-- 设置为minimal表示支持select *,分区字段过滤,Limit等
-- 设置为more表示支持select 任意字段,包括函数,过滤,和limit等
set hive.fetch.task.conversion=more;
本地模式
- 将提交到yarn上运行的任务转换为在本地模式,即单台机器上处理所有的任务。对于小数据集,执行时间可以被明显缩短。
sql
复制代码
-- 开启自动转化为本地模式
set hive.exec.mode.local.auto=true;
-- 区别于set mapreduce.framework.mode=local; 设置mapreduce的执行模式会强制所有sql走本地模式,但设
-- 置自动转化后,会根据输入数据量进行判断是否开启本地模式
-- 设置local MapReduce的最大输入数据量,当输入数据量小于这个值时采用local MapReduce的方式,
-- 默认为134217728,即128M
set hive.exec.mode.local.auto.inputbytes.max=50000000;
-- 设置local MapReduce的最大输入文件个数,当输入文件个数小于这个值时采用local MapReduce的方式,
-- 默认为4
set hive.exec.mode.local.auto.input.files.max=10;
并行执行
- Hive会将一个SQL转化为一个或多个Stage, 每个Stage对应一个MR Job。默认情况下,Hive同时只会执行一个Stage。但是某SQL语句可能会包含多个Stage,但这多个Stage可能并非完全互相依赖,也就是说有些Stage是可以并行执行的。此处提到的并行执行就是指这些Stage的并行执行。
sql
复制代码
--启用并行执行优化
set hive.exec.parallel=true;
--同一个sql允许最大并行度,默认为8
set hive.exec.parallel.thread.number=8;
严格模式
- 将hive.strict.checks.no.partition.filter设置为true时,对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行。换句话说,就是用户不允许扫描所有分区。
- 将hive.strict.checks.orderby.no.limit设置为true时,对于使用了order by语句的查询,要求必须使用limit语句。因为order by为了执行排序过程会将所有的结果数据分发到同一个Reduce中进行处理,强制要求用户增加这个limit语句可以防止Reduce额外执行很长一段时间(开启了limit可以在数据进入到Reduce之前就减少一部分数据)。
- 将hive.strict.checks.cartesian.product设置为true时,会限制笛卡尔积的查询。