hive自定义函数,压缩,存储,调优

今日内容:
1) hive的自定义函数 (简单会实现即可)
2) hive优化部分: 能够理解, 知道有这些优化方案 , 记录
2.1: hive的数据压缩
2.2: hive的数据存储格式
2.3: fetch抓取
2.4: 本地模式
2.5: join的优化
2.6: SQL优化的方案
2.7: 动态分区调整
2.8: 如何解决数据倾斜问题
2.9: 并行执行
2.10: 严格模式
2.11: jvm的重用
2.12: 推测执行
2.13: 存储方式 和压缩方式


1) hive的自定义函数: 当hive自带的内置函数, 无法满足需求, 可以通过自定义函数的方式来解决
UDF函数 : 一进一出 , 大部分的内置函数 都是 UDF函数
UDTF函数: 表生成函数, 一进多出, 例如 explode函数
UDAF函数: 多进一出 , 聚合类型的函数

1.1: 自定义一个 UDF函数 : 实际使用比较多
1) 创建一个项目, 导入相关的依赖
2) 创建一类类, 继承一个UDF类
3) 需要实现evaluate函数;evaluate函数支持重载 : 方法名称 千千万万不要写错了
4) 在这个方法中, 实现自定义函数的功能即可
5) 将这个项目进行打包, 形成一个jar包
6) 将jar包 加载到hive环境中,
7) 使用 自定义函数即可

public class MyUDF extends UDF {
// 方法参数, 表示调度函数 传递的信息, 如果函数, 需要传递多个参数, 这里就定义多个参数, 如果不传递, 不定义
public Text evaluate(final Text s) { // 调用函数的时候, 需要传递一个参数, 这个参数类似是一个文本类型

if(s == null){
return null;
}
//1.获取数据
String str = s.toString();

//2. 将接收到数据, 从小写转换为大写

String upperStr = str.toUpperCase();
return new Text(upperStr);
}
}

1.2: 自定义一个UDTF函数 : 简单了解
步骤和 UDF的步骤, 基本雷同
区别如下:
1) 继承的类: GenericUDTF
2) 重写的方法: process();
3) UDF函数写出去, 直接通过return 而UDTF 函数, 通过 forword()

public class MyUDTF extends GenericUDTF {
private final transient Object[] forwardListObj = new Object[1];
// 初始化方法, 主要是用来规定 函数的输入输出的类型
public StructObjectInspector initialize(StructObjectInspector argOIs) throws UDFArgumentException {
//设置列名的类型
List fieldNames = new ArrayList<>();
//设置列名
fieldNames.add("column_01");
List fieldOIs = new ArrayList() ;//检查器列表

//设置输出的列的值类型
fieldOIs.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);

return ObjectInspectorFactory.getStandardStructObjectInspector(fieldNames, fieldOIs);

}


// 在此方法中, 实现UDTF核心代码 : 方法的参数, 即为调用函数的传递的参数

// 如果输出多个数据, 需要将数据统一封装在一个集合或者数组
@Override
public void process(Object[] objects) throws HiveException {

//1. 获取用户执行函数的传递进来两个参数:
String str = objects[0].toString(); // 内部是一个 Text
String split = objects[1].toString();

//2. 对数据进行切割操作:
String[] split1 = str.split(split);

//3. 遍历 : 输出的操作, 是一行一行的输出操作
for (String word : split1) {
//3. 直接写出去: 每遍历出一个, 写出去一个, 表示写出去一行
forwardListObj[0] = word;
forward(forwardListObj);

}


}


2. hive的调优部分讲解:
2.1: hive的数据压缩的方式:
hive的数据压缩, 其实本质上指的就是对MR中数据进行压缩操作

map端在输出的中间结果文件进行压缩: 减少 reduce从map端拉取的数据体积 , 提升拉取的效率
reduce端, 在输出最终结果文件进行压缩: 减少对磁盘的占用空间


在hive中 实现map 端和 reduce端的数据压缩, 主要从以下这几个配置设置
io.compression.codecs : org.apache.hadoop.io.compress.DefaultCodec (不压缩) 输入类型的压缩算法
取值为:
org.apache.hadoop.io.compress.GzipCodec
org.apache.hadoop.io.compress.BZip2Codec
org.apache.hadoop.io.compress.Lz4Codec
org.apache.hadoop.io.compress.SnappyCodec

mapreduce.map.output.compress : false 表示是否要开启 map端的压缩 默认为 false
mapreduce.map.output.compress.codec: org.apache.hadoop.io.compress.DefaultCodec map采用的何种压缩算法
取值为:
org.apache.hadoop.io.compress.GzipCodec
org.apache.hadoop.io.compress.BZip2Codec
org.apache.hadoop.io.compress.Lz4Codec
org.apache.hadoop.io.compress.SnappyCodec
mapreduce.output.fileoutputformat.compress : false 表示 是否要开启 reduce端的数据压缩 默认为 false
mapreduce.output.fileoutputformat.compress.codec : org.apache.hadoop.io.compress.DefaultCodec reduce采用的何种压缩算法
取值为:
org.apache.hadoop.io.compress.GzipCodec
org.apache.hadoop.io.compress.BZip2Codec
org.apache.hadoop.io.compress.Lz4Codec
org.apache.hadoop.io.compress.SnappyCodec
mapreduce.output.fileoutputformat.compress.type : RECORD 表示压缩的类型
取值:
RECORD: 行压缩
BLOCK : 块压缩 --- 块压缩

如果要开启map端的压缩:
set hive.exec.compress.intermediate=true ; 表示开启中间传输数据压缩
set mapreduce.map.output.compress = true ;
set mapreduce.map.output.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;

如果要开启reduce端的压缩:
set hive.exec.compress.output=true; 开启最终结果压缩
set mapreduce.output.fileoutputformat.compress = true;
set mapreduce.output.fileoutputformat.compress.codec = org.apache.hadoop.io.compress.SnappyCodec;

通用设置:
set mapreduce.output.fileoutputformat.compress.type = BLOCK ;

2.2: hive的数据存储的格式: 将hive中表数据存储在HDFS上的时候, 采用何种格式来存储
默认的格式为 textFile (普通文件)

HDFS(hive)的支持的文件存储格式: textFile SEQUENCEFILE ORC PARQUET
行式存储方案: textFile SEQUENCEFILE

列式存储方案: ORC PARQUET

数仓一共被分为三层:
ODS层: 源数据层 , 主要和数据源打交道
对于ODS层, 表数据的格式, 一般采用 textFile , 从文件 到表的导入操作, 一般使用 load data 操作

而load data 只能适用于 普通文本类型

DW层: 数据仓库层, 主要使用进行数据分析工作
对于DW层, 表数据的格式, 一般采用的 : ORC + snappy的压缩

但是:
ORC内部, 自带了一种压缩格式 GZIP压缩格式, 在使用ORC的时候, 需要将这种格式的内置默认压缩算法
更换为 snappy压缩算法
app(da ads)层: 数据应用层 数据展示层 , 主要是用于存储分析的结果数据
后期如果要对接BI, 可以将结果数据, 从APP层将数据导出到 交互式的数据库(mysql)中,从而提升BI查询数据的效率

对于APP层, 表数据的格式, 一般采用的 textFile


测试, 不同的文件存储格式, 对应压缩比如何: 压缩后文件大小 和 解压的效率
请问压缩方案, 是不是压得越小, 越好呢? 并不是, 在选择压缩方案的时候, 要选择一个压的效率和 解压效率都相对而言比较高


textFile存储文件大小为: 18.1M
ORC存储文件大小为 :2.8M 列式存储, 而且内置了一种ZLIB压缩算法
orc将压缩算法去掉, 不压缩: 7.7 M
PARQUET存储文件大小为: 13.1 M

不同格式存储同一份数据, 最终文件大小比较:

ORC > PARQUET > textFile


不同类型的查询时间比较:

textFile查询的时间为: 14.307 秒
ORC查询的时间为: 10.815
parquet查询的时间为: 12.217

ORC > parquet > textFile


由此可见, 一般采用文件存储格式为 ORC格式


提供在DW层, 构件表的标准建表方案:
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, 没有使用ORC默认的压缩算法形成文件更小, 但是带来好处, 大大提升压缩效率 和 解压效率


以后工作中:
1) dw层采用 ORC + snappy
2) 在查询分析数据的时候, 开启 map 和 reduce端的压缩方案


2.3 : fetch本地抓取策略:
核心: 能不走 MR 就不走MR
在执行查询数据SQL的时候, 能通过MR查询, 就不通过MR查询

通过一个配置来开启本地 fetch抓取策略:
set hive.fetch.task.conversion = more
取值:
minimal
more
none

注意:
如果将这个参数设置为 none 表示所有的SQL查询都会执行MR

如果将这个参数设置为 minimal 表示 在执行 select * | select 字段 的时候, 不会执行MR , 如果hive采用的0.x版本, 采用此默认值

如果将这个参数设置为 more (默认值) , 表示在执行 select * 和 select 字段 以及 limit 和 简单过滤查询,不会执行MR操作
从1.x开始往后, 都是此默认值

2.4: hive的本地模式: 将MR在本地运行, 不让其运行在yarn集群中
主要的指, 运行一些, 数据量比较小的情况下, 此时完全不需要将其上传到yarn集群中, 进行分布式执行操作,
只要将MR运行在yarn上, 最起码需要大约20S时间, 才可以运行完成, 因为在执行之前, 就要耗费大量的时间进行
初始化的动作, 而真正执行数据的时间, 可能只有几秒钟就搞定了

如何开启本地模式:
set hive.exec.mode.local.auto=true;  --开启本地mr 默认为 false

set hive.exec.mode.local.auto.inputbytes.max=134217728; 文件大小的阈值

set hive.exec.mode.local.auto.input.files.max=4; 文件数量的阈值

建议:
在测试环境中, 一般开启本地模式, 再生产环境中, 开不开无所谓


2.5: join的优化: 在进行多表操作的时候, 表与表之间要进行join的操作


两个表进行join:
小表 join 大表: 建议 将 小表放置 join的前面, 这样可以减少join的次数, 提高效率
在hive中, 开启一个 map端的join操作, 让hive将小表的数据, 之间写入到缓存中, 在map端实现
join, 启动多个mapTask读取大表的数据, 然后分别让其余内存中小表数据进行join即可

如何开启 mapJoin操作:
set hive.auto.convert.join = true; 默认已经开启mapJoin
set hive.mapjoin.smalltable.filesize= 25000000; 如果表文件数据小于这个值, 就会认为小表

hive 架构 中提供了优化器, 主要是对SQL或者MR进行优化的, 这个优化器, 在进行join的时候
自动帮我们去识别那个是小表, 那个是大表, 此时并不需要用户吧小表放置在join的前面


大表 join 大表 : 如何解决呢? 解决思路 尽可能的减少join的数量, 能在join之前过滤掉的数据, 就不要放置在join之后

表 A : 商品表
p001 裤子 388.50
p002 衣服 288.5
p003 手机 7888
p004 电脑 9999

表B : 订单表
u001 p002 288.5
u001 p003 7888
u002 p001 388.5
u003 p004 9999
u004 p002 288.5

需求: 请查询 衣服一共买了多少钱?
select sum(表b.price) from 表A join 表 B on 表A.id = 表b.pid where 表A.pname='衣服'
正确方式:
select sum(表b.price) from表 B join (select pid from 表A where pname = '衣服') temp1 on temp1.id = 表b.pid;


如果两个大表中, 有一个大表中join的关联条件中有大量的null值如何办呢?

解决方案:
1) 提前将这些为null的数据, 先过滤掉
2) 如果不允许进行过滤, 必须进行join: 将这些null的值数据, 使用随机数填充
SELECT a.*

FROM nullidtable a
LEFT JOIN ori b ON CASE WHEN a.id IS NULL THEN concat('hive', rand()) ELSE a.id END = b.id;

2.6: SQL 优化:
2.6.1: 列裁剪 :在执行查询的时候, 尽量避免使用 select * 一般都是select 字段操作
2.6.2: 分区裁剪 : 如果查询的表示一个分区表, 请必须使用分区字段来减少筛选的数据
2.6.3: group by : 在执行group by的操作, 如果表中某一个分组下的数据有非常的多, 此时就可能发现数据的倾斜的问题
可以采用在map进行一个局部的汇总(combiner), 然后在通过reduce进行最终汇总操作

如何开启map端的聚合:
set hive.map.aggr = true; 默认值就是 true
set hive.groupby.mapaggr.checkinterval = 100000;

set hive.groupby.skewindata = true; 默认值false 开启自动进行数据的负载均衡
将当前这个MR拆解为 2个MR: 大型局部过程
第一个MR,将所有的数据进行随机发往不同的reduce, 可能会发生, 每个reduce中都有同一个key : 帮助实现局部聚合
第二个MR, 将上一个MR执行结果, 当做这个MR的输入, 然后将相同key发往同一个reduce, 做最终的聚合操作
2.6.4: Count(distinct)操作:
select count( distinct ip) from 表; 这种操作是一个效率极低的操作 , 在执行count操作, 只能运行一个reduce

如何提升这个SQL的效率? 使用 group by
select count(*) from (select ip from 表 group by ip) temp ;

前提: 数据足够的多, 如果数据不多, 请采用 count( distinct ip) 即可 , 因为 采用下面的MR会启动两个MR
2.6.5: 笛卡尔积 : 在操作过程中, 尽可能的避免出现笛卡尔积的现象, 在进行多表join的时候, 千万别少了 on条件


2.7: 动态分区调整 :
作用: 帮助将某一个分区表所有的分区识别出来, 然后将这些各个分区的依次的导入到另一个表中

如何实现动态分区?
1) set hive.exec.dynamic.partition=true; 开启动态分区 默认为 true
2) set hive.exec.dynamic.partition.mode=nonstrict; 将hive的SQL的模式修改为非严格模式
3) set hive.exec.max.dynamic.partitions=1000; 整个hive中分区表, 一共可以有多少个分区 默认 为 1000
4) set hive.exec.max.dynamic.partitions.pernode=100 ; 每个MR最大一次性处理多少个分区
5) set hive.exec.max.created.files=100000; 每个 mrJOB最大一次性可以操作多少个文件
6) set hive.error.on.empty.partition=false; 有空分区, 是否要报异常, 设置为 false ,默认为false

在实现动态分区前, 需要先将 2 3 4 进行修改
3 4 一般修改的大一些

动态分区的SQL:
INSERT overwrite TABLE ori_partitioned_target PARTITION (p_time)
SELECT id, time, uid, keyword, url_rank, click_num, click_url, p_time
FROM ori_partitioned;

注意: 发现动态分区SQL 和之前的导入的SQL差不多的, 有两点需要注意:
1) partition (只需要设置分区的字段是谁)
2) 在select查询的字段, 最后, 必须显现的将分区字段放置咋整个表最后面

2.8: 数据倾斜:
根据数据的倾斜的地方, 动态的调整 map的数量 和 reduce的数量来解决

动态调整map的数量:

如何减少map的数量:
set mapred.max.split.size=112345600;
set mapred.min.split.size.per.node=112345600;
set mapred.min.split.size.per.rack=112345600;
前面三个参数确定合并文件块的大小,大于文件块大小128m的,按照128m来分隔,
--小于128m,大于100m的,按照100m来分隔,把那些小于100m的(包括小文件和分隔大文件剩下的),

如何增加map的数量: 将一个文件块拆分为多个block块
1) set mapred.reduce.tasks=10; 设置reduce的数量为 多个
2) create table a_1 as select * from tab_info distribute by rand(123);
a_1这个表中, 就会有 10个文件了
3) 对 a_1表分析处理, 自然就会有10个mapTask一起干活了

动态调整reduce的数量:
set hive.exec.reducers.bytes.per.reducer=256000000; 通过设置一个reduce能够处理的数据量 默认为256M .
set mapred.reduce.tasks=1; 直接调整reduce数量忽略, 数据的大小


如果计算reduce的数量:
min(maxreduceTask(1009), 总文件大小/一个reduce能够处理数量 );

2.9: hive 并行执行 :
在执行SQL的时候, hive会先将SQL转换为 MapReduce来执行, 在转换MapReduce的时候, 有的SQL操作, 需要分阶段来运行
比如说:
select count(*) from (select ip from 表 group by ip) temp ;
先执行子查询, 第一个MR, 然后得到结果后, 执行外部的查询操作, 这是第二个MR

在一些特殊的SQL的下, 各个阶段联系不是特别强, 可以让各个阶段同时来执行. 比如说 使用union all 连接sql

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

如何来实现并行操作:
set hive.exec.parallel=false;   --是否要开启 任务并行执行
set hive.exec.parallel.thread.number=16; --同一个sql允许最大并行度,默认为8。

前提条件:
1) 一个SQL 各个阶段依赖关系不是特别强
2) yarn集群, 必须有资源, 如果没有资源, 也无法并行

2.10 : hive的严格模式 :
严格模式, 是为了防止用户执行一些可能对效率产生影响的SQL语句

set hive.mapred.mode = strict; --开启严格模式
set hive.mapred.mode = nostrict; --开启非严格模式

能够防止一下三种效率比较低的SQL:
1) 在操作分区表的时候, 没有使用分区字段
2) 在查询数据, 使用order by 进行全表排序, 必须使用 limit 进行获取
3) 防止出现笛卡尔积的现象
2.11 : jvm的重用: 重复利用 container 资源容器
set  mapred.job.reuse.jvm.num.tasks=10;

2.12: hive的推测执行: 一般关闭不使用

你可能感兴趣的:(hive自定义函数,压缩,存储,调优)