Hive函数

2023-11-19 08:48:32 浏览数 (1)

一、Hive建表SQL

Hive建表SQL

二、Hive函数

Hive函数

三、函数

1、查看内置函数

查看系统自带函数

代码语言:javascript复制
show functions;

查看自带函数用法

代码语言:javascript复制
#	显示简单用法
desc function !=;
# 显示详细用法
desc function extended !=;
2、空字段赋值(nvl)

NVL:

给值为NULL的数据赋值,它的格式是NVL( value,default_value)。

功能:

如果value为NULL,则NVL函数返回default_value的值,否则返回value的值如果两个参数都为NULL ,则返回NULL。

代码语言:javascript复制
#	当common为空时,返回age
SELECT nvl(name,'age') FROM user;
#	当name为空时候,用full_name替代
SELECT nvl(name,full_name) FROM user;
3、CASE WHEN THEN ELSE END

根据不同数据,返回不同逻辑

代码语言:javascript复制
SELECT
 CASE deptno
   WHEN 1 THEN Engineering
   WHEN 2 THEN Finance
   ELSE admin
 END,
 CASE zone
   WHEN 7 THEN Americas
   ELSE Asia-Pac
 END
 FROM emp_details

/** 当a=b时,返回c;当a=d时,返回d;当a=e时,放回e;其他情况返回f。*/
CASE a 
	WHEN b THEN c
	WHEN d THEN e
	ELSE f
END

案例:

代码语言:javascript复制
SELECT 
	name,
	CASE sex 
		WHEN '男' THEN 1 
		ELSE 0 
	END
FROM default.user01 u

SELECT 
	name,
	SUM(CASE sex WHEN '男'THEN  1 ELSE 0 END )
FROM default.user01 u 
GROUP BY name ;
4、行转列

将多行数据进行汇总成1条数据、多行转为单行

  • CONCAT(string A/col, string B/col…):多个字段拼接
  • CONCAT_WS(separator, str1, str2,...):多字段拼接(指定拼接符号)
  • COLLECT_SET(col):接受分组数据,汇总为Array(去重)
  • COLLECT_LIST(col):接受分组数据,汇总为Array(不去重)
代码语言:javascript复制
SELECT 
	deptno AS Deptno,
	CONCAT_WS(',' , COLLECT_LIST(Ename))   AS enames
FROM user
GROUP BY deptno

CONCAT

代码语言:javascript复制
# 字符串拼接
#	返回输入字符串连接后的结果,支持任意个输入字符串;
CONCAT(string A/col, string B/col…):

案例:

代码语言:javascript复制
SELECT CONCAT(name,age)  FROM  default.user01 u ;
#	结果:
悟空12
大海14

**CONCAT_WS(separator, str1, str2,...):**多字符串拼接

  • 它是一个特殊形式的 CONCAT()。第一个参数剩余参数间的分隔符。
  • 分隔符可以是与剩余参数一样的字符串。
  • 如果分隔符是 NULL,返回值也将为 NULL。
  • 这个函数会跳过分隔符参数后的任何 NULL 和空字符串。
  • 分隔符将被加到被连接的字符串之间;
  • 注意: CONCAT_WS must be "string or array<string>
代码语言:javascript复制
#	`separator`:连接符
#	`str1`:参数1
#	`str2`:参数2
#	:字符串拼接(CONCAT_WS(' ','a','b')=>a b)
CONCAT_WS(separator, str1, str2,...)

案例:

代码语言:javascript复制
SELECT concat_ws('.', 'www', 'facebook', 'com') FROM default.user01;
www.facebook.com

SELECT concat_ws('.', 'www', array('facebook', 'com')) FROM default.user01;
www.facebook.com
www.facebook.com

COLLECT_SET(col)

  • 函数只接受基本数据类型
  • 它的主要作用是将某字段的值进行去重汇总,产生array类型字段。
  • 去重
代码语言:javascript复制
#	将分组后数据汇总为Array
collect_list(name)

案例:

代码语言:javascript复制
#	["宋宋","凤姐","婷婷"]
select collect_set(name)from default.user01 group by age;
#	宋宋 凤姐 婷婷
select  concat_ws(' ',collect_set(name))from default.user01 group by age;

COLLECT_LIST(col):

  • 不去重
  • 函数指接收基本数据类型
  • 它的主要作用是将某字段的值进行不去重汇总,产生array类型字段。
代码语言:javascript复制
#	将分组后数据汇总为Array
collect_list(name)

案例

代码语言:javascript复制
#	["宋宋","宋宋","凤姐","婷婷"]
select collect_list(name)from default.user01 group by age;
#	宋宋 宋宋 凤姐 婷婷
select  concat_ws(' ',collect_list(name))from default.user01 group by age;
5、列转行
  • EXPLODE(col):将hive表的一列中复杂的array或者map结构拆分成多行。
  • SPLIT(string str, string regex): 按照regex字符串分割str,会返回分割后的字符串数组。
  • LATERAL VIEW:侧视图
    • LATERAL VIEW udtf(expression) tableAlias AS columnAlias
    • 用于和split, explode等UDTF一起使用,它能够将一列数据拆成多行数据,在此基础上可以对拆分后的数据进行聚合。lateral view首先为原始表的每行调用UDTF,UDTF会报一行拆分成一行或者多行,lateral view再把结果组合,产生一个支持别名表的虚拟表。
代码语言:javascript复制
SELECT split(category,",")  FROM movie_info ;
代码语言:javascript复制
SELECT explode(split(category,","))  FROM movie_info mi;
  • split:将category列中数据,拆分为多条,以Array类型。
  • explode:将单列中Array存储的转为多行数据。
  • lateral VIEW:将Array中数据整合为可被查询的列。
代码语言:javascript复制
SELECT movie,category_name 
	FROM movie_info 
lateral VIEW
explode(split(category,",")) movie_info_tmp  AS category_name ;
6、开窗函数
6.1 简介
  • 窗口函数不同于我们熟悉的常规函数及聚合函数,它输入多行数据(一个窗口),为每行数据进行一次计算,返回一个值。
  • 灵活运用窗口函数可以解决很多复杂的问题,如去重、排名、同比及和环比、连续登录等。
6.2 语法

语法:

代码语言:javascript复制
Function(arg1 ……) over([partition by arg1 ……] [order by arg1 ……] [<window_expression>])

rows必须跟在Order by 子句之后,对排序的结果进行限制,使用固定的行数来限制分区中的数据行数量

如果不指定partition by,则不对数据进行分区,换句话说,所有数据看作同一个分区。

如果不指定order by, 则不对各分区进行排序,通常用于那些顺序无关的窗口函数,如sum()。

如果不指定窗口子句:

  • 不指定order by,默认使用分区内所有行,等同于
代码语言:javascript复制
Function() over(rows between unbounded precedeing and unbounded following)
  • 如果指定order by,默认使用分区内第起点到当前行,等同于
代码语言:javascript复制
Function() over(rows between unbounded preceding and current row)

Over()语法

语法

解释(添加)

解释(不添加)

Partition by:

表示将数据先按字段进行分区

不对数据进行分区,换句话说,所有数据看作同一个分区

Order by:

表示将各个分区内的数据按字段进行排序。不指定默认所有行。

则不对各分区进行排序,通常用于那些顺序无关的窗口函数。指定后:从开头行至当前行。

代码语言:javascript复制
partition by:
不指定:则不对数据进行分区,换句话说,所有数据看作同一个分区。

order by:
不指定order by:默认使用分区内所有行,等同于
指定order by:  默认使用分区内第起点到当前行,等同于
  • window_expression语法

语法

解释

n preceding

往前n行

n following

往后n行

current row

当前行

unbounded preceding

从前面的起点开始

unbounded following

到后面的终点结束

代码语言:javascript复制
#   从开头到当前行
ROWS BETWEEN unbounded preceding AND current row

#   昨天至今天
rows between 1 preceding and current row

#   当天到最后一天
rows between current row and unbounded following
6.3 案例

数据表

  • 查询在2017年4月份,到店购买的人,及到店次数
代码语言:javascript复制
select
	name ,
	count(name) over()
from
	business
where
	substring(orderdate, 1, 7)= '2017-04'
group by name;
  • 查询顾客的购买明细,及到当前日期为止的购买总金额
代码语言:javascript复制
select
	name,
	orderdate,
	cost,
	sum(cost)over(partition by name
order by
	orderdate)
from business;

执行结果

代码语言:javascript复制
jack	2017-01-01	10	10
jack	2017-01-05	46	56
jack	2017-01-08	55	111
jack	2017-02-03	23	134
jack	2017-04-06	42	176
mart	2017-04-08	62	62
  • 每一行数据都新增一列,即消费总额
代码语言:javascript复制
select
	name,
	orderdate,
	cost,
	sum(cost) over() sample1
from business;

执行结果:

代码语言:javascript复制
mart	2017-04-13	94	661
neil	2017-06-12	80	661
mart	2017-04-11	75	661
neil	2017-05-10	12	661
mart	2017-04-09	68	661
mart	2017-04-08	62	661
jack	2017-01-08	55	661
  • 计算每个人的销售总额
    • 以name分区计算,每行数据新增一列,即每人消费总额
代码语言:javascript复制
select
	name,
	orderdate,
	cost ,
	sum(cost)over(partition by name)
from business;

执行结果:

代码语言:javascript复制
jack	2017-01-05	46	176
jack	2017-01-08	55	176
jack	2017-01-01	10	176
jack	2017-04-06	42	176
jack	2017-02-03	23	176
mart	2017-04-13	94	299
mart	2017-04-11	75	299
mart	2017-04-09	68	299
  • 计算每个人截止到当天的销售总额
    • 以name分区、日期排序计算,每行数据增一列,即截止到当前的消费总额,也就是从起点到当前行做聚合。
    • 如果指定order by,默认使用分区内第起点到当前行。
代码语言:javascript复制
select
	name,
	orderdate,
	cost,
	sum(cost)over(partition by name order by orderdate)as sum_now
from
	business;
代码语言:javascript复制
select
	name,
	orderdate,
	cost,
	sum(cost)over(partition by name order by orderdate 
# 	固定写法,表明从开头到当前行
                rows between unbounded preceding and current row )as sum_now
from
	business;

执行结果:

代码语言:javascript复制
jack	2017-01-01	10	10
jack	2017-01-05	46	56
jack	2017-01-08	55	111
jack	2017-02-03	23	134
jack	2017-04-06	42	176
mart	2017-04-08	62	62
mart	2017-04-09	68	130
  • 每个人连续两天的消费总额
    • 以name分区、日期排序计算,每行数据增一列,即连续两天的消费总额也就是前一行和当前行聚合。
    • 当天的值,为当天值与昨天值相加。
代码语言:javascript复制
select 
	name,
	orderdate,
	cost, 
	sum(cost) over(partition by name order by orderdate rows between 1 PRECEDING and current row ) as sample5
from business;

执行结果

代码语言:javascript复制
jack	2017-01-01	10	10
jack	2017-01-05	46	56   (10 46=56)
jack	2017-01-08	55	101  (46 55=101)
jack	2017-02-03	23	78		(55 23=78)
jack	2017-04-06	42	65
mart	2017-04-08	62	62
mart	2017-04-09	68	130
  • 每个人从当天到最后一天的消费总额
    • 以name分区、日期排序计算,每行数据增一列,即当前天到最后一天的消费总额,也就是当前行聚合最后一行。
    • 行,从当前行至最后一行总和。
代码语言:javascript复制
select 
	name,
	orderdate,
	cost,
	sum(cost) over(partition by name order by orderdate rows between current row and UNBOUNDED FOLLOWING )
from business;

计算结果:

代码语言:javascript复制
jack	2017-01-01	10	176
jack	2017-01-05	46	166
jack	2017-01-08	55	120	(120=42 23 55)
jack	2017-02-03	23	65	(65=42 23)
jack	2017-04-06	42	42	(42=42)
mart	2017-04-08	62	299
mart	2017-04-09	68	237
6.4 LAG函数
  • Lag函数用于统计窗口内往上第n行值。
  • scalar_pexpression:列名。
  • offset :为往上几行。
  • default :是设置的默认值(当往上第n行为NULL时,取默认值,否则就为NULL)
代码语言:javascript复制
LAG  (scalar_expression [,offset] [,default]) OVER ([query_partition_clause] order_by_clause);
  • 查看顾客上次的购买时间
代码语言:javascript复制
select
	name,
	orderdate,
	cost,
	lag(orderdate,1,'1900-01-01') over(partition by name order by	orderdate ) as last_time
from	business ;

执行结果:

代码语言:javascript复制
jack	2017-01-01	10	1900-01-01
jack	2017-01-05	46	2017-01-01
jack	2017-01-08	55	2017-01-05
jack	2017-02-03	23	2017-01-08
jack	2017-04-06	42	2017-02-03
mart	2017-04-08	62	1900-01-01
mart	2017-04-09	68	2017-04-08
mart	2017-04-11	75	2017-04-09
6.5 Ntile函数
  • 为已排序的行,均分为指定数量的组,组号按顺序排列,返回组号,不支持rows between
代码语言:javascript复制
select
	name,
	orderdate,
	cost,
	ntile(5) over( order by orderdate) sorted
from business;

执行结果:(分为了5组,下面取第1组数据即可)

代码语言:javascript复制
jack	2017-01-01	10	1
tony	2017-01-02	15	1
tony	2017-01-04	29	1
jack	2017-01-05	46	2
tony	2017-01-07	50	2
jack	2017-01-08	55	2
jack	2017-02-03	23	3
jack	2017-04-06	42	3
mart	2017-04-08	62	3
mart	2017-04-09	68	4
mart	2017-04-11	75	4
mart	2017-04-13	94	4
neil	2017-05-10	12	5
neil	2017-06-12	80	5

取第一组数据

代码语言:javascript复制
select
	t1.name,
	t1.orderdate,
	t1.cost
from
	(select
		name,
		orderdate,
		cost,
		ntile(5) over( order by orderdate) sorted
	from business ) t1
where
	t1.sorted = 1;

执行结果:

代码语言:javascript复制
OK
jack	2017-01-01	10
tony	2017-01-02	15
tony	2017-01-04	29
6.6 Rank
  • Rank():排序,值相同时会重复,总数不会变(1、1、3、4)。
  • DENSE_RANK():排序,值相同时会重复,总数会较少(1、1、2、3)。
  • ROW_NUMBER():根据顺序计算。字段相同就按排头字段继续排(1、2、3、4)。

源数据:

7、自定义函数
  • **内置函数:**Hive自带的函数。
  • **自定义函数:**当Hive提供的内置函数无法满足你的业务处理需要时。可以自己定义一些函数。
    • UDF(User-Defined-Function): 一进一出。
    • UDAF(User-Defined Aggregation Function):聚合函数,多进一出,类似:count/max/min
    • UDTF(User-Defined Table-Generating Functions):炸裂函数,一进多出,如:explode()

四、压缩和存储

1、简介
  • Hive不会强制要求将数据转换成特定的格式才能使用。利用Hadoop的InputFormat API可以从不同数据源读取数据,使用OutputFormat API可以将数据写成不同的格式输出。
  • 对数据进行压缩虽然会增加额外的CPU开销,但是会节约客观的磁盘空间,并且通过减少载入内存的数据量而提高I/O吞吐量会更加提高网络传输性能。
  • 原则上Hadoop的job时I/O密集型的话就可以采用压缩可以提高性能,如果job是CPU密集型的话,那么使用压缩可能会降低执行性能。
2、压缩简介

常用压缩算法

压缩格式

算法

文件扩展名

是否可切分

对应的编码/解码器

Deflate

Deflate

.deflate

org.apache.hadoop.io.compress.DefaultCodec

Gzip

Deflate

.gz

org.apache.hadoop.io.compress.GzipCodec

Bzip2

Bzip2

.bz2

org.apache.hadoop.io.compress.BZip2Codec

Lzo

Lzo

.lzo

com.hadoop.compression.lzo.LzopCodec

Snappy

Snappy

.snappy

org.apache.hadoop.io.compress.SnappyCodec

压缩效率对比

压缩算法

原始文件大小

压缩文件大小

压缩速度

解压速度

gzip

8.3GB

1.8GB

17.5MB/s

58MB/s

bzip2

8.3GB

1.1GB

2.4MB/s

9.5MB/s

LZO

8.3GB

2.9GB

49.3MB/s

74.6MB/s

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

参数

默认值

阶段

建议

io.compression.codecs (在core-site.xml中配置)

org.apache.hadoop.io.compress.DefaultCodec, org.apache.hadoop.io.compress.GzipCodec, org.apache.hadoop.io.compress.BZip2Codec,org.apache.hadoop.io.compress.Lz4Codec

输入压缩

Hadoop使用文件扩展名判断是否支持某种编解码器

mapreduce.map.output.compress

false

mapper输出

这个参数设为true启用压缩

mapreduce.map.output.compress.codec

org.apache.hadoop.io.compress.DefaultCodec

mapper输出

使用LZO、LZ4或snappy编解码器在此阶段压缩数据

mapreduce.output.fileoutputformat.compress

false

reducer输出

这个参数设为true启用压缩

mapreduce.output.fileoutputformat.compress.codec

org.apache.hadoop.io.compress. DefaultCodec

reducer输出

使用标准工具或者编解码器,如gzip和bzip2

mapreduce.output.fileoutputformat.compress.type

RECORD

reducer输出

SequenceFile输出使用的压缩类型:NONE和BLOCK

3、Map输出阶段压缩

开启map输出阶段压缩可以减少job中map和Reduce task间数据传输量。

(1)开启hive中间传输数据压缩功能

代码语言:javascript复制
set hive.exec.compress.intermediate =true;

(2)开启mapreduce中map输出压缩功能

代码语言:javascript复制
set mapreduce.map.output.compress=true;

(3)设置mapreduce中map输出数据的压缩方式

代码语言:javascript复制
set mapreduce.map.output.compress.codec= org.apache.hadoop.io.compress.SnappyCodec;

(4)执行查询语句

代码语言:javascript复制
select count(ename) name from emp;

查看历史日志中,有压缩方式.snappy

4、开启Reduce输出阶段压缩

当Hive将输出写入到表中时可以通过属性hive.exec.compress.output,对输出内容进行压缩。

hive.exec.compress.output = false,这样输出就是非压缩的纯文本文件了。

hive.exec.compress.output = true,来开启输出结果压缩功能。

(1)开启hive最终输出数据压缩功能

代码语言:javascript复制
set hive.exec.compress.output=true;

(2)开启mapreduce最终输出数据压缩

代码语言:javascript复制
set mapreduce.output.fileoutputformat.compress=true;

(3)设置mapreduce最终数据输出压缩方式

代码语言:javascript复制
set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;

(4)设置mapreduce最终数据输出压缩为块压缩

代码语言:javascript复制
set mapreduce.output.fileoutputformat.compress.type=BLOCK;

(5)测试一下输出结果是否是压缩文件

代码语言:javascript复制
insert overwrite local directory '/opt/module/hive/datas/distribute-result' select * from score distribute by name sort by score desc;
5、文件存储格式
5.1 列式存储和行式存储

Hive支持的存储数据的格式主要有:

  • TEXTFILE:行存储
  • SEQUENCEFILE:行存储
  • ORC:列存储
  • PARQUET:列存储

如图所示左边为逻辑表,右边第一个为行式存储,第二个为列式存储。

  • 行存储的特点

查询满足条件的一整行数据的时候

列存储则需要去每个聚集的字段找到对应的每个列的值

行存储只需要找到其中一个值,其余的值都在相邻地方

所以此时行存储查询的速度更快。

  • 列存储的特点

每个字段的数据聚集存储,查询只需要少数几个字段的时候,能大大减少读取的数据量;

每个字段的数据类型一定是相同的,列式存储可以针对性的设计更好的设计压缩算法。

5.2 TextFile_行存储
  • 默认格式,数据不做压缩,磁盘开销大,数据解析开销大。
  • 可结合Gzip、Bzip2使用,但使用Gzip这种方式,hive不会对数据进行切分,从而无法对数据进行并行操作。
5.3 Orc_列存储

Orc (Optimized Row Columnar)是Hive 0.11版里引入的新的存储格式。

如下图所示可以看到每个Orc文件由1个或多个stripe组成,每个stripe一般为HDFS的块大小,每一个stripe包含多条记录,这些记录按照列进行独立存储,对应到Parquet中的row group的概念。

  • 每个Stripe里有三部分组成,分别是Index Data,Row Data,Stripe Footer:
    • Index Data:一个轻量级的index,默认是每隔1W行做一个索引。这里做的索引应该只是记录某行的各字段在Row Data中的offset。
    • Row Data:存的是具体的数据,先取部分行,然后对这些行按列进行存储。对每个列进行了编码,分成多个Stream来存储。
    • Stripe Footer:存的是各个Stream的类型,长度等信息。每个文件有一个File Footer,这里面存的是每个Stripe的行数,每个Column的数据类型信息等;每个文件的尾部是一个PostScript,这里面记录了整个文件的压缩类型以及FileFooter的长度信息等。在读取文件时,会seek到文件尾部读PostScript,从里面解析到File Footer长度,再读FileFooter,从里面解析到各个Stripe信息,再读各个Stripe,即从后往前读。

ORC中压缩:

  • Zlib:压缩比高,效率低。压缩ORC的默认压缩格式。
  • Snappy:压缩比低,效率高。
5.4 Parquet_列存储

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

  • (1)行组(Row Group):每一个行组包含一定的行数,在一个HDFS文件中至少存储一个行组,类似于orc的stripe的概念。
  • (2)列块(Column Chunk):在一个行组中每一列保存在一个列块中,行组中的所有列连续的存储在这个行组文件中。一个列块中的值都是相同类型的,不同的列块可能使用不同的算法进行压缩。
  • (3)页(Page):每一个列块划分为多个页,一个页是最小的编码的单位,在同一个列块的不同页可能使用不同的编码方式。

通常情况下,在存储Parquet数据的时候会按照Block大小设置行组的大小,由于一般情况下每一个Mapper任务处理数据的最小单位是一个Block,这样可以把每一个行组由一个Mapper任务处理,增大任务执行并行度。

上图展示了一个Parquet文件的内容,一个文件中可以存储多个行组,文件的首位都是该文件的Magic Code,用于校验它是否是一个Parquet文件,Footer length记录了文件元数据的大小,通过该值和文件长度可以计算出元数据的偏移量,文件的元数据中包括每一个行组的元数据信息和该文件存储数据的Schema信息。

除了文件中每一个行组的元数据,每一页的开始都会存储该页的元数据,在Parquet中,有三种类型的页:数据页、字典页和索引页。数据页用于存储当前行组中该列的值,字典页存储该列值的编码字典,每一个列块中最多包含一个字典页,索引页用来存储当前行组下该列的索引,目前Parquet中还不支持索引页。

5.5 数据存储大小对比
  • TextFile
代码语言:javascript复制
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;
  • ORC
代码语言:javascript复制
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
tblproperties("orc.compress"="NONE"); // 由于ORC格式时自带压缩的,这设置orc存储不使用压缩
  • Parquet
代码语言:javascript复制
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 ;

同样数据文件存储后大小:

  • TextFile:18.1M
  • ORC:7.7 M
  • Parquet:13.1M

存储的大小对比:ORC > Parquet > textFile

查询速度:速度接近

6、存储和压缩结合

Key

Default

Notes

orc.compress

ZLIB

high level compression (one of NONE, ZLIB, SNAPPY)

orc.compress.size

262,144

number of bytes in each compression chunk

orc.stripe.size

268,435,456

number of bytes in each stripe

orc.row.index.stride

10,000

number of rows between index entries (must be >= 1000)

orc.create.index

true

whether to create row indexes

orc.bloom.filter.columns

“”

comma separated list of column names for which bloom filter should be created

orc.bloom.filter.fpp

0.05

false positive probability for bloom filter (must >0.0 and <1.0)

注意:所有关于ORCFile的参数都是在HQL语句的TBLPROPERTIES字段里面出现。

  • ZLIB压缩的ORC存储方式(2.8 M)
代码语言:javascript复制
create table log_orc_zlib(
	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"="ZLIB");
  • SNAPPY压缩的ORC存储方式(3.7 M)
代码语言:javascript复制
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压缩的parquet存储方式(6.4 MB)
代码语言:javascript复制
create table log_parquet_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 parquet
tblproperties("parquet.compression"="SNAPPY");
  • hive表的数据存储格式一般选择:orc或parquet。
  • 压缩方式一般选择snappy,lzo。
  • ORC默认压缩:Zlib

四、企业级调优

1、查看执行计划
代码语言:javascript复制
EXPLAIN [EXTENDED | DEPENDENCY | AUTHORIZATION] query
  • 没有生成MR任务的
代码语言:javascript复制
explain select * from emp;
  • 生成MR任务的
代码语言:javascript复制
explain select deptno, avg(sal) avg_sal from emp group by deptno;

查看详细执行计划

代码语言:javascript复制
explain extended select * from emp;
2、Hive建表优化
  • 分区表
  • 分桶表
  • 合适的文件格式
3、HQL语法优化
3.1 列裁剪和分区裁剪

在生产环境中,会面临列很多或者数据量很大时,如果使用select * 或者不指定分区进行全列或者全表扫描时效率很低。Hive在读取数据时,可以只读取查询中所需要的列,忽视其他的列,这样做可以节省读取开销(中间表存储开销和数据整合开销)

  • 列裁剪:在查询时只读取需要的列。
  • 分区裁剪:在查询中只读取需要的分区。
3.2 Group By
  • 默认情况下,Map阶段同一Key数据分给一个Reduce,当一个Key数据过大时就倾斜了。
  • 并不是所有的聚合操作都需要再Reduce端完成,很多聚合操作都可以先在Map端进行部分聚合,最后在Reduce端得出最终结果。

参数配置

  • 开启Map端聚合设置
代码语言:javascript复制
#	开启在Map端聚合
set hive.map.aggr = true
#	Map端聚合条目数目
set hive.groupby.mapaggr.checkinterval = 100000
#	开启数据倾斜时,进行负载均衡
set hive.groupby.skewindata = true
  • 当开启数据负载均衡时,生成的查询计划会有2个MRJob。 第一个MRJob中,Map的输出结果会随机分布到Reduce中,每个Reduce做部分聚合操作,并输出结果,这样处理的结果是相同的Group By Key有可能被分发到不同的Reduce中,从而达到负载均衡的目的; 第二个MRJob再根据预处理的数据结果按照Group By Key分布到Reduce中(这个过程可以保证相同的Group By Key被分布到同一个Reduce中),最后完成最终的聚合操作。
3.3 CBO优化
4、数据倾斜
4.1 现象

数据倾斜:绝大多数任务很快完成,只有1个或者几个任务执行的很忙甚至最终执行失败。

数据过量:所有的任务执行都很慢。这时候只有提高执行资源才能优化HQL的执行效率。

原因:

按照Key分组后,少量的任务负载着绝大部分的数据的计算,也就是说。产生数据倾斜的HQL中一定存在着分组的操作,所以从HQL的角度,我们可以将数据倾斜分为单表携带了Group By字段的查询和2表(多表)Join的查询。

4.2 单表数据倾斜优化

1)使用参数优化

当任务中存在Group By操作同时聚合函数为count或者sum。可以设置参数来处理数据倾斜的问题。

代码语言:javascript复制
#	是否在Map端进行聚合,默认为True
set hive.map.aggr = true
#	在Map端进行聚合操作的条目数目
set hive.groupby.mapaggr.checkinterval = 100000
#	有数据倾斜的时候进行负载均衡(默认是false)
set hive.groupby.skewindata = true

2)增加Reduce数量

当数据中多个key同时导致数据倾斜,可以通过增加Reduce的数量解决数据倾斜问题。

  • 方式一(动态调整):
代码语言:javascript复制
#	每个Reduce处理的数据量默认为 256M(参数1)
set hive.exec.reducers.bytes.per.reducer=256000000
#	每个任务最大的Reduce数,默认为1009(参数2)
set hive.exec.reducers.max=1009
#	计算Reducer数的公式
N = min(参数2 ,总数入数据量/参数1)
  • 方式二(直接指定):
代码语言:javascript复制
#	直接指定Reduce个数
set mapreduce.job.reduces = 15;
4.3 Join数据倾斜优化

1)使用参数

在编写Join查询语句时,如果确定是由于join出现的数据倾斜:

代码语言:javascript复制
# join的键对应的记录条数超过这个值则会进行分拆,值根据具体数据量设置
set hive.skewjoin.key=100000;
# 如果是join过程出现倾斜应该设置为true
set hive.optimize.skewjoin=false;

如果开启了,在Join过程中Hive会将计数超过阈值hive.skewjoin.key(默认100000)的倾斜key对应的行临时写进文件中,然后再启动另一个job做map join生成结果。通过 hive.skewjoin.mapjoin.map.tasks参数还可以控制第二个job的mapper数量,默认10000。

代码语言:javascript复制
set hive.skewjoin.mapjoin.map.tasks=10000;

2)大小表join

可以使用MapJoin,没有Reduce阶段就不会出现数据倾斜。

3)大表大表Join

使用打散加扩容的方式解决数据倾斜问题,选择其中较大的表做打算操作

代码语言:javascript复制
SELECT * ,concat(id,'_' , '0 or 1 or 2')FROM A; t1

选择其中较小的表做扩容处理

代码语言:javascript复制
SELECT *,concat(id,'-','0') from B
UNION ALL
SELECT *,concat(id,'-','1') from B
UNION ALL
SELECT *,concat(id,'-','2') from B;t2
5、Hive Job优化

0 人点赞