文章目录
一 自定义函数
Hive的内置函数不可能覆盖所有的需求
根据用户自定义函数类别分为以下三种:
(1)UDF(User-Defined-Function)
一进一出
(2)UDAF(User-Defined Aggregation Function)
聚集函数,多进一出
类似于:count/max/min
(3)UDTF(User-Defined Table-Generating Functions)
一进多出
如lateral view explore()
1 UDF函数
-
创建Maven项目
套路:
写API:new对象,做事情,关对象
写插件:继承点什么,实现点什么
-
导入项目
<dependencies> <!-- https://mvnrepository.com/artifact/org.apache.hive/hive-exec --> <dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>3.1.2</version> </dependency> </dependencies>
-
编写代码
package com.hike.hive; import org.apache.hadoop.hive.ql.exec.UDFArgumentException; import org.apache.hadoop.hive.ql.exec.UDFArgumentLengthException; import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException; import org.apache.hadoop.hive.ql.metadata.HiveException; import org.apache.hadoop.hive.ql.udf.generic.GenericUDF; import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.PrimitiveObjectInspector; import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory; /** * 返回字符串的长度 */ public class MyUDF extends GenericUDF { /** * 检查输入的方法以及约束输出的类型 * @param objectInspectors 输入参数的检查器,有几个参数就有几个参数的检查器 * @return 输出的参数检查器 * @throws UDFArgumentException */ public ObjectInspector initialize(ObjectInspector[] objectInspectors) throws UDFArgumentException { if(objectInspectors.length != 1){ throw new UDFArgumentLengthException("错误的参数数量"); } if(!objectInspectors[0].getCategory().equals(ObjectInspector.Category.PRIMITIVE)){ throw new UDFArgumentTypeException(0, "参数类型不一致"); } return PrimitiveObjectInspectorFactory.javaIntObjectInspector; } /** * 实现逻辑的方法 * @param deferredObjects * @return * @throws HiveException */ public Object evaluate(DeferredObject[] deferredObjects) throws HiveException { Object o = deferredObjects[0].get(); if(o == null){ return 0; } return o.toString().length(); } /** * 如果方法执行过程中出现问题,显示错误提示信息 * @param strings * @return */ public String getDisplayString(String[] strings) { return null; } }
-
将打包好的jar包上传服务器上的/opt/module/hive/lib目录下
-
重启hive服务,连接数据库
hiveservices.sh restart beeline -u jdbc:hive2://hadoop101:10000 -n hike
-
创建临时函数
create temporary function my_len as "com.hike.hive.MyUDF";
-
执行临时函数
select my_len("123");
不是重点,写的时候回来找文档即可
二 压缩和存储
1 MR支持的压缩编码
压缩格式 | 工具 | 算法 | 文件扩展名 | 是否可切分 |
---|---|---|---|---|
DEFLATE | 无 | DEFLATE | .deflate | 否 |
Gzip | gzip | DEFLATE | .gz | 否 |
bzip2 | bzip2 | bzip2 | .bz2 | 是 |
LZO | lzop | LZO | .lzo | 是 |
Snappy | 无 | Snappy | .snappy | 否 |
snappy是目前最流行的压缩编码,因为此种压缩方式更加快速。
1 开启Map输出阶段压缩
开启map输出阶段压缩可以减少job中map和Reduce task间数据传输量。
-
开启hive中间传输数据压缩功能
set hive.exec.compress.intermediate=true;
-
开启mapreduce中map输出压缩功能
set mapreduce.map.output.compress=true;
-
设置mapreduce中map输出数据的压缩方式
set mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec;
-
执行查询语句
hive (default)> select count(ename) name from emp;
2 开启Reduce输出阶段压缩
当Hive将输出写入到表中时,输出内容同样可以进行压缩。属性hive.exec.compress.output控制着这个功能。用户可能需要保持默认设置文件中的默认值false,这样默认的输出就是非压缩的纯文本文件了。用户可以通过在查询语句或执行脚本中设置这个值为true,来开启输出结果压缩功能。
-
开启hive最终输出数据压缩功能
set hive.exec.compress.output=true;
-
开启mapreduce最终输出数据压缩
set mapreduce.output.fileoutputformat.compress=true;
-
设置mapreduce最终数据输出压缩方式
set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;
-
设置mapreduce最终数据输出压缩为块压缩
set mapreduce.output.fileoutputformat.compress.type=BLOCK;
-
测试一下输出结果是否是压缩文件
insert overwrite local directory '/opt/module/datas/distribute-result' select * from emp distribute by deptno sort by empno desc;
一般在输入端使用压缩,在输出端使用较少
3 比较各种文件存储格式
Hive支持的存储数据的格式主要有:TEXTFILE 、SEQUENCEFILE、ORC、PARQUET。
前两者称为行式存储,后两者称为列式存储,前者将一张表格按照行存储到磁盘上,后者按照列存储到磁盘上。
两者存储方式的优劣要根据具体使用场景进行判断。
(1)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,即从后往前读。
(2)Parquet格式
Parquet文件是以二进制方式存储,不可以直接读取,文件中包括该文件的数据和元数据,因此Parquet格式文件是自解析的。
行组(Row Group):每一个行组包含一定的行数,在一个HDFS文件中至少存储一个行组,类似于orc的stripe的概念。
列块(Column Chunk):在一个行组中每一列保存在一个列块中,行组中的所有列连续的存储在这个行组文件中。一个列块中的值都是相同类型的,不同的列块可能使用不同的算法进行压缩。
页(Page):每一个列块划分为多个页,一个页是最小的编码的单位,在同一个列块的不同页可能使用不同的编码方式。
通常情况下,在存储Parquet数据的时候会按照Block大小设置行组的大小,由于一般情况下每一个Mapper任务处理数据的最小单位是一个Block,这样可以把每一个行组由一个Mapper任务处理,增大任务执行并行度。
一个文件中可以存储多个行组,文件的首位都是该文件的Magic Code,用于校验它是否是一个Parquet文件,Footer length记录了文件元数据的大小,通过该值和文件长度可以计算出元数据的偏移量,文件的元数据中包括每一个行组的元数据信息和该文件存储数据的Schema信息。除了文件中每一个行组的元数据,每一页的开始都会存储该页的元数据,在Parquet中,有三种类型的页:数据页、字典页和索引页。数据页用于存储当前行组中该列的值,字典页存储该列值的编码字典,每一个列块中最多包含一个字典页,索引页用来存储当前行组下该列的索引,目前Parquet中还不支持索引页。
(3)主流文件存储格式对比
-
创建表,存储数据格式为TEXTFILE
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 ; --指定文件存储格式,不指定时默认为文本格式
-
向表中插入数据
load data local inpath '/opt/module/datas/log.data' into table log_text ;
-
创建表,存储数据格式为ORC
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"); --不启用压缩
-
向表中插入数据
insert into table log_orc select * from log_text ;
-
创建表,存储数据格式为parquet
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 ;
-
查看三个文件的大小
dfs -du -h /user/hive/warehouse/文件名 ;
-
存储文件的压缩比总结:
ORC > Parquet > textFile
4 比较各种文件压缩格式
参数在sql语句的tblproperities字段中进行设置
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) |
(1)创建一个非压缩的的ORC存储方式
--创建表格
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");
--插入数据
insert into log_orc_zlib select * from log_text;
--查看数据
dfs -du -h /user/hive/warehouse/log_orc_zlib/ ;
(2)创建一个SNAPPY压缩的ORC存储方式
--创建表格
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");
--插入数据
insert into log_orc_snappy select * from log_text;
--查看数据
dfs -du -h /user/hive/warehouse/log_orc_snappy/ ;
(3)创建一个SNAPPY压缩的parquet格式
--创建表格
create table log_par_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("parqute.compress"="SNAPPY");
--插入数据
insert into log_par_snappy select * from log_text;
--查看数据
dfs -du -h /user/hive/warehouse/log_par_snappy/ ;
hive表的数据存储格式一般选择:orc或parquet。压缩方式一般选择snappy,lzo。
hive配套数据