Bootstrap

Hive调优

hive官方配置url: Configuration Properties - Apache Hive - Apache Software Foundation

hive命令和参数配置

hive参数配置的意义: 开发Hive应用/调优时,不可避免地需要设定Hive的参数。设定Hive的参数可以调优HQL代码的执行效率,或帮助定位问题。然而实践中经常遇到的一个问题是,为什么我设定的参数没有起作用?这是对hive参数配置几种方式不了解导致的!

hive参数设置范围 :

        配置文件参数 >   命令行参数  >   set参数声明

hive参数设置优先级:

        set参数声明  >   命令行参数   >  配置文件参数


注意: 一般执行SQL需要指定的参数, 都是通过 set参数声明 方式进行配置,因为它属于当前会话的临时设置,断开后就失效了

配置文件

Hive的配置文件包括    

        用户自定义配置文件:$HIVE_CONF_DIR/hive-site.xml

        默认配置文件:$HIVE_CONF_DIR/hive-default.xml

        注意: 用户自定义配置会覆盖默认配置。

另外,Hive也会读入Hadoop的配置,因为Hive是作为Hadoop的客户端启动的,Hive的配置会覆盖Hadoop的配置。 配置文件的设定对本机启动的所有Hive进程都有效。

命令行参数

        启动Hive(客户端或Server方式)时,可以在命令行添加-hiveconf param=value来设定参数,

        例如:hive --service hiveserver2 --hiveconf hive.root.logger=DEBUG,console

        注意:只要通过beeline连接的这个hiveserver2服务的客户端 都具备了这个配置信息 这一设定的作用域是所有请求的Sessions会话有效。

参数声明

        可以在HQL中使用SET关键字设定参数,

        例如:set mapreduce.job.reduces = 3;

        这一设定的作用域仅是当前session会话的。

1.hive数据压缩

为什么需要压缩

结论:以CPU换整体效率

来感受一下:

1000MB文件,从A传输到B,花费10秒(网速100MB/s)

1000MB文件

        压缩到100MB花费3秒(压缩效率330MB/s)

        从A传输到B花费1秒

        在B解压花费3秒

        耗时7秒

结论是: 在CPU和磁盘性能尚可的前提下,采用压缩可以提高:网络传输效率、磁盘利用率 压缩就是以额外的CPU消耗(压缩、解压缩)换取网络和磁盘的高效率。

Hive的数据压缩

Hive底层是运行MapReduce,所以Hive支持什么压缩格式本质上取决于MapReduce。 

MapReduce的支持如下表:

压缩格式

工具

算法

文件扩展名

是否可切分

DEFAULT

DEFAULT

.deflate

Gzip

gzip

DEFAULT

.gz

bzip2

bzip2

bzip2

.bz2

LZO

lzop

LZO

.lzo

LZ4

LZ4

.lz4

Snappy

Snappy

.snappy

压缩对比

在后续可能会使用GZ(GZIP), 保证压缩后的数据更小, 同时压缩和解压的速度比较OK的,

但是大部分的选择主要会选择另一种压缩方案, snappy, 此种方案可以保证在合理的压缩比下, 拥有更高的解压缩的速度

snappy | A fast compressor/decompressor

On a single core of a Core i7 processor in 64-bit mode, Snappy compresses at about 250 MB/sec or more and decompresses at about 500 MB/sec or more.

压缩配置参数

要在Hadoop中启用压缩,可以配置如下参数(mapred-site.xml文件中):

开启压缩

开启map输出阶段压缩可以减少job中map和Reduce task间数据传输量. 当Hive将输出写入到表中时,输出内容同样可以进行压缩。用户可以通过在查询语句或执行脚本中设置这个值为true,来开启输出结果压缩功能。

开启压缩(map阶段或者reduce阶段)
开启hive支持中间结果的压缩方案

set hive.exec.compress.intermediate; -- 查看默认

执行操作并查看执行后的结果 

set hive.exec.compress.intermediate=true ;


开启hive支持最终结果压缩

set hive.exec.compress.output; -- 查看默认

 执行操作并查看执行后的结果 

set hive.exec.compress.output=true;


开启MR的map端压缩操作

set mapreduce.map.output.compress; -- 查看默认

 执行操作并查看执行后的结果 

set mapreduce.map.output.compress=true;


设置mapper端压缩的方案

set mapreduce.map.output.compress.codec; -- 查看默认

 执行操作并查看执行后的结果 

set mapreduce.map.output.compress.codec= org.apache.hadoop.io.compress.SnappyCodec;


开启MR的reduce端的压缩方案

set mapreduce.output.fileoutputformat.compress; -- 查看默认

执行操作并查看执行后的结果  

set mapreduce.output.fileoutputformat.compress=true;


设置reduce端压缩的方案

set mapreduce.output.fileoutputformat.compress.codec; -- 查看默认

 执行操作并查看执行后的结果 

set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;


设置reduce的压缩类型

set mapreduce.output.fileoutputformat.compress.type; -- 查看默认

 执行操作并查看执行后的结果 

set mapreduce.output.fileoutputformat.compress.type=BLOCK;

 压缩格式练习:

存储格式应用对比

-- 演示textfile行存储格式: 18.1 m
create table log_text (
    track_time string,
    url string,
    session_id string,
    referer string,
    ip string,
    end_user_id string,
    city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE ; -- TEXTFILE当前默认的,可以省略
-- 加载数据
load data local inpath '/mnt/' into table log_text;
-- 查询数据
select * from log_text;

数据内存18.1 m

演示orc列存储(默认zlib): 2.78 m

create table log_orc(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS orc ;-- 默认内置一种压缩算法:ZLIB
-- 加载数据(先上传数据文件到根目录)
insert into table log_orc select * from log_text;
-- 查询数据
select * from log_orc;

数据内存2.78 m

[重点orc配合snappy]
演示orc列存储(指定snappy): 3.75 m

create table log_orc_snappy(
   track_time string,
   url string,
   session_id string,
   referer string,
   ip string,
   end_user_id string,
   city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS orc tblproperties ("orc.compress"="SNAPPY"); -- 配合SNAPPY压缩
-- 加载数据(先上传数据文件到根目录)
insert into table log_orc_snappy select * from log_text;
-- 查询数据
select * from log_orc_snappy;

数据内存3.75 m

/*ORC文件格式的数据, 默认内置一种压缩算法:ZLIB , 在实际生产中一般会将ORC压缩算法替换为 snappy
格式为: STORED AS orc tblproperties ("orc.compress"="SNAPPY") */


演示parquet压缩存储:13.09 m

create table log_parquet(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS PARQUET ;
-- 加载数据(先上传数据文件到根目录)
insert into table log_parquet select * from log_text;
-- 查询数据
select * from log_parquet;

数据内存13.09 m

拓展dfs -du -h

-- 查看hdfs文件大小除了去页面查看,还可以通过命令

hdfs dfs -du -h '/user/hive/warehouse/hive05.db/log_text/log.data' ;
hdfs dfs -du -h '/user/hive/warehouse/hive05.db/log_orc/000000_0' ;
hdfs dfs -du -h '/user/hive/warehouse/hive05.db/log_orc_snappy/000000_0' ;
hdfs dfs -du -h '/user/hive/warehouse/hive05.db/log_parquet/000000_0' ;

2.行列存储原理

行存储的特点: 查询满足条件的一整行数据的时候,列存储则需要去每个聚集的字段找到对应的每个列的值,行存储只需要找到其中一个值,其余的值都在相邻地方,所以此时行存储查询的速度更快。
列存储的特点: 因为每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量;每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。

行存储: textfilesquencefile的存储格式都是基于行存储的;
    优点: 每行数据连续存储              select * from 表名; 查询速度相对较快
    缺点: 每列类型不一致,空间利用率不高   select 列名 from 表名; 查询速度相对较慢
列存储: ORC(Optimized Row Columnar)和PARQUET都是基于列存储的
    优点: 每列数据连续存储         select 列名 from 表名;  查询速度相对较快
    缺点: 因为每行数据不是连续存储  select * from 表名;查询速度相对较慢

注意: ORC文件格式的数据, 默认内置一种压缩算法:zlib , 在实际生产中一般会将ORC压缩算法替换为 snappy使用,格式为: STORED AS orc tblproperties ("orc.compress"="SNAPPY") 

TEXTFILE格式

默认格式,数据不做压缩,磁盘开销大,数据解析开销大。

可结合Gzip、Bzip2使用(系统自动检查,执行查询时自动解压),

但使用这种方式,hive不会对数据进行切分,从而无法对数据进行并行操作。

创建表,存储数据格式为TEXTFILE:18.1M左右 

-- 演示textfile行存储格式: 18.1 m
create table log_text (
    track_time string,
    url string,
    session_id string,
    referer string,
    ip string,
    end_user_id string,
    city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS TEXTFILE ; -- TEXTFILE当前默认的,可以省略
-- 加载数据
load data local inpath '/mnt/' into table log_text;

 ORC格式

ORC的全称是(Optimized Record Columnar),使用ORC文件格式可以提高hive读、写和处理数据的能力。

在ORC格式的hive表中,记录首先会被横向的切分为多个stripes,然后在每一个stripe内数据以列为单位进行存储,所有列的内容都保存在同一个文件中。每个stripe的默认大小为256MB,相对于之前RCFile每个4MB而言,更大的stripe使ORC的数据读取更加高效。

ORC在RCFile的基础上进行了一定的改进,所以与RCFile相比,具有以下一些优势:

1、ORC中的特定的序列化与反序列化操作可以使ORC file writer根据数据类型进行写出。

2、提供了多种RCFile中没有的indexes,这些indexes可以使ORC的reader很快的读到需要的数据,并且跳过无用数据,这使得ORC文件中的数据可以很快的得到访问。

3、由于ORC file writer可以根据数据类型进行写出,所以ORC可以支持复杂的数据结构(比如Map等)。

4、除了上面三个理论上就具有的优势之外,ORC的具体实现上还有一些其他的优势,比如ORC的stripe默认大小更大,为ORC writer提供了一个memory manager来管理内存使用情况。

创建表,存储数据格式为ORC : 2.8 M左右

create table log_orc(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS orc ;-- 默认内置一种压缩算法:ZLIB

PARQUET格式

Parquet是面向分析型业务的列式存储格式,由Twitter和Cloudera合作开发,2015年5月从Apache的孵化器里毕业成为Apache顶级项目。

Parquet文件是以二进制方式存储的,所以是不可以直接读取的,文件中包括该文件的数据和元数据,因此Parquet格式文件是自解析的。

Parquet 在同一个数据文件中保存一行中的所有数据,以确保在同一个节点上处理时一行的所有列都可用。Parquet 所做的是设置 HDFS 块大小和最大数据文件大小为 1GB,以确保 I/O 和网络传输请求适用于大批量数据。

Parquet文件在磁盘所有数据分成多个RowGroup 和 Footer。

1.RowGroup: 真正存储数据区域,每一个RowGroup存储多个ColumnChunk的数据。

2.ColumnChunk就代表当前RowGroup某一列的数据,因为可能这一列还在其他RowGroup有数据。ColumnChunk可能包含一个Page。

3.Page是压缩和编码的单元,主要包括PageHeader,RepetitionLevel,DefinitionLevel和Values.

4.PageHeader: 包含一些元数据,诸如编码和压缩类型,有多少数据,当前page第一个数据的偏移量,当前Page第一个索引的偏移量,压缩和解压的大小

5.DefinitionLevel: 当前字段在路径中的深度

6.RepetitionLevel: 当前字段是否可以重复 Footer:主要当前文件的元数据和一些统计信息

创建表,存储数据格式为parquet: 13.1M左右

create table log_parquet(
track_time string,
url string,
session_id string,
referer string,
ip string,
end_user_id string,
city_id string
)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
STORED AS PARQUET ;

3. fetch抓取[练习]

核心点: 在执行SQL, 能不走MR, 尽量不走MR

回想, 在之前执行什么样查询的SQL的时候,没有走MR呢?

1) 全表扫描

2) 查询某个列数据

3) 执行一些简答查询操作

4) 执行limit操作

而这些操作, 没有走MR原因, 就是hive默认以及开启本地抓取的策略方案:

hive.fetch.task.conversion:   设置本地抓取策略

hive.fetch.task.conversion默认是more,老版本hive默认是minimal, 该属性修改为more以后,在全局查找、字段查找、limit查找等都不走mapreduce。
可选:

more (默认值): 可以保证在执行全表扫描, 查询某几个列, 进度limit操作,还有简单条件查询4种情况都不会走MR
minimal :  保证执行全表扫描以,查询某几个列,简单limit操作,3种情况可以不走MR
none : 全部的查询的SQL 都执行MR

示例:

默认是more,底层自动调优

set hive.fetch.task.conversion; -- 默认结果是more

set hive.fetch.task.conversion = more;

select * from log_text; -- 不走mr
select url from log_text;-- 不走mr
select url from log_text limit 3;-- 不走mr
select url from log_text where city_id = -1;-- 不走mr


可以体验下minimal模式

set hive.fetch.task.conversion = minimal;

select * from log_text;-- 不走mr
select url from log_text;-- 不走mr
select url from log_text limit 3;-- 不走mr
select url from log_text where city_id = -1;-- 走mr


还可以体验下none模式

set hive.fetch.task.conversion = none;

select * from log_text;-- 走mr
select url from log_text;-- 走mr
select url from log_text limit 3;-- 走mr
select url from log_text where city_id = -1;-- 走mr

4. local本地模式[练习]

核心点:让MR能走本地模式, 尽量走本地MR(大白话: 小活能自己干就不要麻烦别人)

大多数的Hadoop Job是需要Hadoop提供的完整的可扩展性来处理大数据集的。不过,有时Hive的输入数据量是非常小的。在这种情况下,为查询触发执行任务时消耗可能会比实际job的执行时间要多的多。对于大多数这种情况,Hive可以通过本地模式在单台机器上处理所有的任务。对于小数据集,执行时间可以明显被缩短。 用户可以通过设置hive.exec.mode.local.auto的值为true,来让Hive在适当的时候自动启动这个优化。

查看默认值:

set hive.exec.mode.local.auto

如何开启:

set hive.exec.mode.local.auto=true;  -- 默认值为: false

​开启本地模式后, 在什么情况下执行本地MR:   只有当输入的数据满足以下两个特性后, 才会执行本地MR

设置local mr的最大输入数据量,当输入数据量小于这个值时采用local  mr的方式,默认为134217728,即128M

-- 设置本地MR最大处理的数据量
set hive.exec.mode.local.auto.inputbytes.max=51234560;   -- 默认为 128M

设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式,默认为4

-- 设置本地MR最大处理的文件的数量
set hive.exec.mode.local.auto.input.files.max=10; -- 默认值为4

示例:

4.开启本地mr(默认false,如果小数据任务进行调优开启,小任务能自己干就自己干)

set hive.exec.mode.local.auto;  -- 默认关闭
set hive.exec.mode.local.auto=false; -- 手动关闭
set hive.exec.mode.local.auto=true; -- 手动开启

设置local mr的最大输入数据量,当输入数据量小于这个值时采用local  mr的方式,默认为134217728,即128M

set hive.exec.mode.local.auto.inputbytes.max;-- 查看
set hive.exec.mode.local.auto.inputbytes.max=134217728;

设置local mr的最大输入文件个数,当输入文件个数小于这个值时采用local mr的方式,默认为4

set hive.exec.mode.local.auto.input.files.max;-- 查看
set hive.exec.mode.local.auto.input.files.max=4;

执行sql语句

-- 没有开启本地执行24秒,开启后1.5秒
select * from log_text order by city_id desc;

注意: 有的靓仔开启本地模式后执行上述sql语句,会报code 2...错误
-- 错误:[08S01][2] Error while processing statement: FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask
-- 如何解决? /export/server/hive/conf/hive-env.sh  修改 export HADOOP_HEAPSIZE=2048
-- 注意: 修改完后需要重启hive服务

5. join的优化操作

Map端Join

如果不指定MapJoin或者不符合MapJoin的条件,那么Hive解析器会将Join操作转换成在Reduce阶段完成join。

容易发生数据倾斜。可以用MapJoin把小表全部加载到内存在map端进行join,避免reducer处理。

设置map端join

默认为true  开启mapJoin支持

set hive.auto.convert.join = true;

设置 小表的最大的数据量  20多M

set hive.mapjoin.smalltable.filesize= 25000000;

在当下的hive版本中,大表join小表或者小表join大表,就算是关闭map端join的情况下,基本上没有区别了(hive为了解决数据倾斜的问题,会自动进行过滤)。

大表Join大表 - 空KEY过滤

有时join超时是因为某些key对应的数据太多,而相同key对应的数据都会发送到相同的reducer上,从而导致内存不够 如何解决?

此时我们应该仔细分析这些异常的key,在join之前, 能提前对数据进行过滤, 那么就提前过滤掉, 这样可能会满足小表的阈值, 也可以执行mapJoin,即使满足不了, 可以减少join之间的数据量, 从而提升效率

大表Join大表 - 空KEY转换

        有时虽然某个key为空对应的数据很多,但是相应的数据不是异常数据,必须要包含在join的结果中,此时我们可以表a中key为空的字段赋一个随机的值,使得数据随机均匀地分不到不同的reducer上。例如:

不随机分布:这样的后果就是所有为null值的id全部都变成了相同的字符串,所有的key相同,相同key的数据会到同一个reduce当中去,及其容易造成数据的倾斜

随机分布:我们可以通过hive的rand函数,随机的给每一个为空的id赋上一个随机值,这样就不会造成数据倾斜

6. 列裁剪

Hive在读数据的时候,可以只读取查询中所需要用到的列,而忽略其他列

例如:

假设有一个表A: a b c d e   5个字段, 请查看以下SQL
select  a,b from A where a=xxx;

在这条SQL, 发现没有使用c d e 字段, 在from A表时候, 读取数据, 只需要将a列 和 b列数据读取出来即可, 不需要读取cde列字段, 这样可以减少读取的数据量, 从而提升效率

如何配置呢?

列裁剪(只读取sql语句需要的字段,节省读取开销,提升效率)
默认就是true  (在hive 2.x中无需在配置了, 直接为固定值: true)

set hive.optimize.cp=true;

7. 分区裁剪

执行查询SQL的时候, 能在join之前提前进行条件过滤的操作, 一定要提前过滤, 不要在join后进行过滤操作

如果操作的表是一张分区表, 那么一定要带上分区字段, 以减少扫描的数据量, 从而提升效率

例如:

select  * from A join B where A.id=xxx;

优化后:

select * from (select * from A where id= xxx) A join B;

注意:Hive自动执行这种裁剪优化

如何配置呢?

分区裁剪

默认为就是true (在hive 2.x中无需在配置了, 直接为固定值: true)

set hive.optimize.pruner=true;

8. group by 操作

执行分组操作, 翻译后的MR, 分组的字段就是k2的字段, 按照k2进行分组操作, 将相同value合并在同一个集合中, 既然分组的字段就是MR的k2, 那么分区也会按照分组字段进行分区操作, 如果某个组下数据非常的多, 可能出现出现什么问题呢?

此时有可能发生数据倾斜, 因为相同key会发往同一个reduce中
所以说: 在hive中出现数据倾斜的主要体现在两个方面:
    第一个:执行join操作(reduce join)
    第二个:执行group by 操作

如何在hive中配置:

方案一:  
        (1)是否在Map端进行聚合,默认为True

set hive.map.aggr = true;

        (2)在Map端进行聚合操作的条目数目

set hive.groupby.mapaggr.checkinterval = 100000;

方案二:  官方称为 负载均衡
        (3)有数据倾斜的时候进行负载均衡(默认是false)

set hive.groupby.skewindata = true;

        第一个MR Job中,Map的输出结果会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的;
        第二个MR Job再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。

9. count(distinct)

说明 : count(distinct) 在数据量比较大的情况下, 效率并不高

思考: 你知道是为什么吗?

原因如下:
        请问1: 执行count操作的时候, hive翻译的MR, reduce数量是否可以有多个? 必然不会有多个, 只能有一个, 因为全局求最终结果
        此时如果执行统计的时候, 需要进行去重,那么去重工作是由reduce执行去重操作,  由于reduce只有一个, 所有的数据都在一个reduce中, 此时reduce的压力比较大

    希望执行去重工作可能有多个reduce一起来执行操作, 此时可以将SQL优化:
        原有:

select count(distinct ip) from ip_tab;

        优化:

select
    count(ip)
       from
(select ip from ip_tab group by ip) tmp;

        请注意: 这样的做法, 虽然会运行两个MR, 但是当数据量足够庞大的时候, 此操作绝对是值得的, 如果数据量比较少, 此操作效率更低

count(distinct)优化

set hive.optimize.countdistinct; -- 默认就是true
set hive.optimize.countdistinct = true;

/*
SELECT count(DISTINCT id) FROM 大表;
结果:
SELECT count(id) FROM (SELECT id FROM 大表 GROUP BY id) a;
*/

select count(distinct devicetype) from device1;
select count(devicetype) from (select devicetype from device1 group by devicetype) a;

注意: 小表拆分两个mr反而效率低,以后大表的时候才会真正提升效率

10. 笛卡尔积

        什么是笛卡尔积呢? 在进行join的时候, 两个表乘积之后结果就是笛卡尔积的结果

        比如: 一个表有5条, 一个表有3条数据, 笛卡尔积结果就有15条数据 , 笛卡尔积中有大量数据都是无用数据

        什么时候会产生笛卡尔积呢? 在多表join的时候, 关联条件缺少或者使用错误的关联条件以及将关联条件放置在where中都会导致笛卡尔积

在实际使用中, 建议:

1) 避免join的时候不加on条件,或者无效的on条件
2) 关联条件不要放置在where语句, 因为底层, 先产生笛卡尔积 然后基于where进行过滤 , 建议放置on条件上
3) 如果实际开发中无法确定表与表关联条件 建议与数据管理者重新对接, 避免出现问题

11. 动态分区[练习]

需求: 请将下面的一个分区表数据, 拷贝到另一个分区表, 保证对应区数据放置到另一个表的对应区下

如何配置呢?

作用:  帮助一次性灌入多个分区的数据
参数:

set hive.exec.dynamic.partition.mode=nonstrict;  -- 开启非严格模式 默认为 strict(严格模式)
set hive.exec.dynamic.partition=true;  -- 开启动态分区支持, 默认就是true

可选的参数:

在所有执行MR的节点上,最大一共可以创建多少个动态分区。

set  hive.exec.max.dynamic.partitions=1000;

每个执行MR的节点上,最大可以创建多少个动态分区

set hive.exec.max.dynamic.partitions.pernode=100;

整个MR Job中,最大可以创建多少个HDFS文件

set hive.exec.max.created.files=100000; 

示例:

回顾快速创建表
1.根据score.txt数据以及格式创建表

create table score_part(
   name string,
   subject string,
   score int
)partitioned by (year string)
row format delimited fields terminated by '\t';

2.上传score.txt到hdfs中
注意: 分区表需要把文件上传到分区目录中,但是一开始没有目录,所以文件上传到任意目录后,等有了分区目录再使用load移动即可
需要使用load方式生成分区目录并且插入数据

load data inpath '/score.txt' into table score_part partition (year='2022');

3.hive根据元数据和分区目录中业务数据映射成表

select * from score_part;

方式1: 先复制表结构再插入数据
复制表结构,原来的是分区表,复制后也是分区表

create table score_part1 like score_part;

不指定分区字段进行动态分区

insert into score_part1 select * from score_part;

方式2: 一步到位
弊端: 但是作为普通表使用了

create table score_part2 as select * from score_part;

方式3: 动态分区
关闭严格模式

set hive.exec.dynamic.partition.mode=nonstrict;

复制表结构,原来的是分区表,复制后也是分区表

create table score_part3 like score_part;

指定分区字段进行动态分区(必须是非严格模式)

insert into score_part3 partition (year) select * from score_part;

12. 如何调整map和reduce的数量

1>是不是map数越多越好? 答案是否定的。如果一个任务有很多小文件(远远小于块大小128m),则每个小文件也会被当做一个块,用一个map任务来完成,而一个map任务启动和初始化的时间远远大于逻辑处理的时间,就会造成很大的资源浪费。而且,同时可执行的map数是受限的。

2>是不是保证每个map处理接近128m的文件块,就高枕无忧了? 答案也是不一定。比如有一个127m的文件,正常会用一个map去完成,但这个文件只有一个或者两个小字段,却有几千万的记录,如果map处理的逻辑比较复杂,用一个map任务去做,肯定也比较耗时。

3>是不是reduce数越多越好? 答案是否定的。如果reduce设置的过大,对整个作业会产生一定的影响。 ①过多的启动和初始化reduce也会消耗时间和资源; ②另外,有多少个reduce,就会有多少个输出文件,如果生成了很多个小文件,那么如果这些小文件作为下一个任务的输入,则也会出现小文件过多的问题;

4>在什么情况下, 只能有一个reduce呢?

以下几种, 不管如何设置, 最终翻译后reduce只能有一个 1) 执行order by操作 2) 执行不需要group by直接聚合的操作 3) 执行笛卡尔积

  • 如何调整mapTask数量:

    小文件场景:当input的文件都很小,把小文件进行合并归档,减少map数, 设置map数量:

    -- 每个Map最大输入大小(这个值决定了合并后文件的数量)

    set mapred.max.split.size=256000000;

    -- 一个节点上split的至少的大小(这个值决定了多个DataNode上的文件是否需要合并)

    set mapred.min.split.size.per.node=1;

    -- 一个交换机下split的至少的大小(这个值决定了多个交换机上的文件是否需要合并)

    set mapred.min.split.size.per.rack=1;

    -- 执行Map前进行小文件合并默认CombineHiveInputFormat

    set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;

    大文件场景:当input的文件都很大,任务逻辑复杂,map执行非常慢的时候,可以考虑增加Map数,来使得每个map处理的数据量减少,从而提高任务的执行效率。

    举例:如果表a只有一个文件,大小为120M,但包含几千万的记录,如果用1个map去完成这个任务,肯定是比较耗时的, 这种情况下,我们要考虑将这一个文件合理的拆分成多个,这样就可以用多个map任务去完成。 set mapred.reduce.tasks=10; create table a_1 as select * from tab_info distribute by rand(123); 这样会将a表的记录,随机的分散到包含10个文件的a_1表中,再用a_1代替上面sql中的a表,则会用10个map任务去完成。

  • 如何reduce的数量:

    -- 查看reduces数量
    -- 该值默认为-1,由hive自己根据任务情况进行判断。

    set mapred.reduce.tasks;
    set mapreduce.job.reduces;

    -- (1)每个Reduce处理的数据量默认是256MB左右

    set hive.exec.reducers.bytes.per.reducer=256000000;

    -- (2)每个任务最大的reduce数,默认为1009;

set hive.exec.reducers.max=1009;

13.并行执行

在执行一个SQL语句的时候, SQL会被翻译为MR, 一个SQL有可能被翻译成多个MR, 那么在多个MR之间, 有些MR之间可能不存在任何的关联, 此时可以设置让这些没有关联的MR 并行执行, 从而提升效率 , 默认是 一个一个来

如何配置:

打开任务并行执行,默认关闭

set hive.exec.parallel=false;  

同一个sql允许最大并行度,默认为8。

set hive.exec.parallel.thread.number=8;

前提:
    服务器必须有资源, 如果没有 即使支持并行, 也没有任何作用

案例:

select  * from A ....
union all
select * from B ...;

例如:

select from (select * from A group by ...) tmp1 join (select * from B group by xxx) on ...

14. 严格模式[练习]

hive提供一种严格模式, 主要目的, 是为了限制一些 效率极低的SQL放置其执行时间过长, 影响其他的操作

屏蔽一下操作:
1) 执行order by 不加 limit
2) 出现笛卡尔积的现象SQL
3) 查询分区表, 不带分区字段

前提: 数据量足够大, 如果数据量比较少, 严格模式对此三项内容不生效

如何配置:

开启严格模式

set hive.mapred.mode = strict; 

开启非严格模式   最新默认

set hive.mapred.mode = nostrict;

开启严格模式可以禁止3种类型的查询。

1)对于分区表,除非where语句中含有分区字段过滤条件来限制范围,否则不允许执行

        换句话说,就是用户不允许扫描所有分区。进行这个限制的原因是,通常分区表都拥有非常大的数据集,而且数据增加迅速。没有进行分区限制的查询可能会消耗令人不可接受的巨大资源来处理这个表。

2)对于使用了order by语句的查询,要求必须使用limit语句。

        因为order by为了执行排序过程会将所有的结果数据分发到同一个Reducer中进行处理,强制要求用户增加这个LIMIT语句可以防止Reducer额外执行很长一段时间。

3)限制笛卡尔积的查询。

        对关系型数据库非常了解的用户可能期望在执行JOIN查询的时候不使用ON语句而是使用where语句,这样关系数据库的执行优化器就可以高效地将WHERE语句转化成那个ON语句。不幸的是,Hive并不会执行这种优化,因此,如果表足够大,那么这个查询就会出现不可控的情况。

列:

分区表查询没有分区字段
如果不开启严格模式可以执行,开启严格模式后就报错

select * from score_part where score > 60;

分区表查询添加了分区字段
开启严格模式后依然能执行成功

select * from score_part where year = 2022;

15. JVM 重用

        JVM重用是Hadoop调优参数的内容,其对Hive的性能具有非常大的影响,特别是对于很难避免小文件的场景或task特别多的场景,这类场景大多数执行时间都很短。

        Hadoop的默认配置通常是使用派生JVM来执行map和Reduce任务的。这时JVM的启动过程可能会造成相当大的开销,尤其是执行的job包含有成百上千task任务的情况。

        此操作, 在hive2.x默认支持已经不需要配置了

        默认情况下, container资源容器  只能使用一次,不能重复使用, 开启JVM重用, 运行container容器可以被重复使用,在hive2.x已经默认支持了

16. 推测执行

        在分布式集群环境下,因为程序Bug(包括Hadoop本身的bug),负载不均衡或者资源分布不均等原因,会造成同一个作业的多个任务之间运行速度不一致,有些任务的运行速度可能明显慢于其他任务(比如一个作业的某个任务进度只有50%,而其他所有任务已经运行完毕),则这些任务会拖慢作业的整体执行进度。为了避免这种情况发生,Hadoop采用了推测执行(Speculative Execution)机制,它根据一定的法则推测出“拖后腿”的任务,并为这样的任务启动一个备份任务,让该任务与原始任务同时处理同一份数据,并最终选用最先成功运行完成任务的计算结果作为最终结果。

        hadoop中默认两个阶段都开启了推测执行机制。

        hive本身也提供了配置项来控制reduce-side的推测执行:

set hive.mapred.reduce.tasks.speculative.execution=true;

        关于调优推测执行机制,还很难给一个具体的建议。如果用户对于运行时的偏差非常敏感的话,那么可以将这些功能关闭掉。如果用户因为输入数据量很大而需要执行长时间的map或者Reduce task的话,那么启动推测执行造成的浪费是非常巨大。

17.执行计划explain

使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。帮助我们了解底层原理,hive调优,排查数据倾斜等有很有帮助

使用示例:explain [...]  sql查询语句;

explain sql语句: 查看执行计划的基本信息

(1)stage dependencies:各个stage之间的依赖性
包含多个stage阶段,例如 Stage-1和Stage-0,Stage-1 是根stage,Stage-0 依赖 Stage-1,
(2)stage plan:各个stage的执行计划
包含两部分: map端执行计划树和reduce端执行计划树

;