Hive函数及性能优化
- Hive函数分类
- 内置函数
- 标准函数
- 字符函数
- 类型转换函数
- 数学函数
- 日期函数
- 集合函数
- 条件函数
- 聚合函数
- 表生成函数
- Hive UDF:自定义标准函数
- Hive UDF实现流程
- Hive事务
- 概述
- Hive事务的特点和局限
- Hive事务的开启和设置
- Hive PLSQL
- Hive性能调优工具 - EXPLAIN
- Hive性能调优工具 - ANALYZE
- Hive优化设计
- Job优化
- 本地模式运行
- JVM重用(JVM Reuse)
- 并行执行
- 查询优化
- 压缩算法
Hive函数分类
- 从输入输出角度分类
标准函数:一行数据中的一列或多列为输入,结果为单一值
聚合函数:多行的零列到多列为输入,结果为单一值
表生成函数:零个或多个输入,结果为多列或多行 - 从实现方式分类
- 1、内置函数
- 2、自定义函数:
- 2.1、UDF:自定义标准函数
- 2.2、UDAF:自定义聚合函数
- 2.3、UDTF:自定义表生成函数
内置函数
Hive提供大量内置函数供开发者使用
标准函数
字符函数
例:
#将customers表中所有顾客姓名转换成大写
select upper(concat(customer_fname,"·",customer_lname)) from customers
类型转换函数
例:
select customer_street,binary(customer_street,"latin")rst from customers
数学函数
#将orders表中订单金额保留两位小数
select round(order_item_subtotal,2) from order_items
日期函数
select from_unixtime(1600740000,"yyyy-MM-dd HH:mm:ss.S")rst1, unix_timestamp()rst2, unix_timestamp("1970-01-01 08:00:00")rst3,
to_date("2020-09-22 09:43:20")rst4, datediff("2020-09-22 09:43:20","2020-09-22 23:43:20")rst5,
date_add("2020-09-22 09:43:20",-1)rst6, date_format("2020-09-22 09:43:20","yyyy/MM/dd HH:mm:ss")rst7
集合函数
条件函数
select if(1=2,1,2)rst1, nvl(null,"abc")rst2, isnull(null)rst3, isnotnull("null")rst4
聚合函数
count、sum、max、min、avg、var_samp等
例:
#统计orders表中月度订单数量
select date_format(order_date,"yyyy-MM"),count(distinct order_id) from orders group by date_format(order_date,"yyyy-MM")
表生成函数
输出可以作为表使用
例:
select explode(str_to_map(customer_street,":"," ")) from customers
Hive UDF:自定义标准函数
Hive UDF实现流程
- 1、创建Java Maven工程,用Java继承UDF类编写UDF函数(evaluate()方法)(一个类一个方法)
配置文件内容:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>1.2.1</version>
</dependency>
Java代码部分:
TestUDF类:
package cn.kgc.kb09.testudf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
/**
* @Qianchun
* @Date 2020-09-22
* @Description
*/
public class TestUDF extends UDF {
public Text evaluate(Text str) {
if (null == str) {
return null;
}
return new Text(str.toString().toUpperCase());
}
// public static void main(String[] args) {
// TestUDF tu=new TestUDF();
// Text rst=tu.evaluate(new Text(args[0]));
// System.out.println(rst);
// }
}
AddHour类:
package cn.kgc.kb09.testudf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Qianchun
* @Date 2020-09-22
* @Description
*/
public class AddHour extends UDF {
public Text evaluate(Text beforeDate, IntWritable hours) throws Exception {
//把接收到的字符串和整形转成Java的类型
String d=beforeDate.toString();
String[] dAndT=d.split(" ");
String[] day=dAndT[0].split("-");
String[] time=dAndT[1].split(":");
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date=sdf.parse(d);
String rst=sdf.format(new Date(date.getTime()+hours.get()*60*60*1000));
return new Text(rst);
}
// public static void main(String[] args) throws Exception {
// AddHour ah=new AddHour();
// Text t=new Text("2020-09-22 10:20:00");
// IntWritable iw=new IntWritable(3);
// System.out.println(ah.evaluate(t, iw));
// }
}
HourDiff类:
package cn.kgc.kb09.testudf;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Qianchun
* @Date 2020-09-22
* @Description
*/
public class HourDiff extends UDF {
public IntWritable evaluate(Text date1,Text date2) throws Exception{
String d1=date1.toString();
String d2=date2.toString();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date dt1=sdf.parse(d1);
Date dt2=sdf.parse(d2);
long diff=dt1.getTime()-dt2.getTime();
int rst=(int)(diff/1000/60/60);
return new IntWritable(rst);
}
// public static void main(String[] args) throws Exception{
// HourDiff hd=new HourDiff();
// System.out.println(hd.evaluate(new Text("2020-09-21 12:00:00"),
// new Text("2020-09-22 23:00:00")));
// }
}
- 2、打fat包(包括所有依赖文件)
- 3、把jar包上传到Linux上
(前提准备:
yum install -y zip
zip -d testudf.jar 'META-INF/.SF' 'META-INF/.RSA' 'META-INF/*SF'
–临时udf函数
- 4、在hive命令行中使用add jar jar包路径即可加载到临时系统中
add jar /root/testudf.jar
- 5、create temporary function 函数名 as ‘方法的全类名’;
create temporary function add_hour as 'cn.kgc.kb09.testudf.AddHour';
–永久udf函数
- 4、在Linux命令行使用hdfs命令把jar上传到hdfs的路径
hdfs dfs -mkdir -p /apps/hive/functions
hdfs dfs -put testudf.jar /apps/hive/functions
- 5、create function 函数名 as ‘方法的全类名’ using jar ‘jar包的hdfs路径’;
create function demo as 'cn.kgc.kb09.testudf.TestUDF' using jar 'hdfs:apps/hive/functions/testudf.jar';
create function hour_diff as 'cn.kgc.kb09.testudf.HourDiff' using jar 'hdfs:apps/hive/functions/testudf.jar';
Hive事务
概述
- 事务(Transaction )指一组单元化操作,这些操作要么都执行,要么都不执行
- ACID特性
Atomicity:原子性
Consistency:一致性
Isolation:隔离性
Durability:持久性
Hive事务的特点和局限
- V0.14版本开始支持行级事务
支持INSERT、DELETE、UPDATE(v2.2.0开始支持Merge)
文件格式只支持ORC - 局限
表必须是bucketed表
需要消耗额外的时间、资源和空间
不支持开始、提交、回滚、桶或分区列上的更新
锁可以为共享锁或排它锁(串联的而不是并发)
不允许从一个非ACID连接读写ACID表
使用较少
Hive事务的开启和设置
- 通过Hive命令行方式设置,当前session有效
#通过命令行方式开启事务
set hive.support.concurrency = true;
set hive.enforce.bucketing = true;
set hive.exec.dynamic.partition.mode = nonstrict;
set hive.txn.manager = org.apache.hadoop.hive.ql.lockmgr.DbTxnManager;
set hive.compactor.initiator.on = true;
set hive.compactor.worker.threads = 1;
- 通过配置文件设置,全局有效
#通过配置文件hive-site.xml
<property>
<name>hive.support.concurrency</name>
<value>true</value>
</property>
<property>
<name>hive.txn.manager</name> <value>org.apache.hadoop.hive.ql.lockmgr.DbTxnManager</value>
</property>
- 通过UI工具(如Ambari)设置
Hive PLSQL
- Hive PLSQL:Hive存储过程(v2.0之后)
支持SparkSQL和Impala
兼容Oracle、DB2、MySQL、TSQL标准
使将现有的过程迁移到Hive变得简单和高效
使编写UDF不需要Java技能
它的性能比Java UDF稍微慢一些
功能较新 - 在Hive2 bin目录下运行./hplsql
Hive性能调优工具 - EXPLAIN
Hive性能调优工具 - ANALYZE
- ANALYZE:分析表数据,用于执行计划选择的参考
收集表的统计信息,如行数、最大值等
使用时调用该信息加速查询 - 语法
ANALYZE TABLE employee COMPUTE STATISTICS;
ANALYZE TABLE employee_partitioned
PARTITION(year=2014, month=12) COMPUTE STATISTICS;
ANALYZE TABLE employee_id COMPUTE STATISTICS
FOR COLUMNS employee_id;
使用分区表、桶表
使用索引
使用适当的文件格式,如orc, avro, parquet
使用适当的压缩格式,如snappy
考虑数据本地化 - 增加一些副本
避免小文件
使用Tez引擎代替MapReduce
使用Hive LLAP(在内存中读取缓存)
考虑在不需要时关闭并发
Job优化
本地模式运行
- Hive支持将作业自动转换为本地模式运行
当要处理的数据很小时,完全分布式模式的启动时间比作业处理时间要长
#通过以下设置开启本地模式
SET hive.exec.mode.local.auto=true; --default false
SET hive.exec.mode.local.auto.inputbytes.max=50000000;
SET hive.exec.mode.local.auto.input.files.max=5; --default 4
- Job必须满足以下条件才能在本地模式下运行
Job总输入大小小于 hive.exec.mode.local.auto. inputbytes.max
map任务总数小于 hive.exec.mode.local.auto. input.files.max
所需的Reduce任务总数为1或0
JVM重用(JVM Reuse)
- 通过JVM重用减少JVM启动的消耗
默认每个Map或Reduce启动一个新的JVM
Map或Reduce运行时间很短时,JVM启动过程占很大开销
通过共享JVM来重用JVM,以串行方式运行MapReduce Job
适用于同一个Job中的Map或Reduce任务
对于不同Job的任务,总是在独立的JVM中运行
#通过以下设置开启JVM重用
set mapred.job.reuse.jvm.num.tasks = 5; -- 默认值为1
并行执行
- 并行执行可提高集群利用率
Hive查询通常被转换成许多按默认顺序执行的阶段
这些阶段并不总是相互依赖的
它们可以并行运行以节省总体作业运行时间
如果集群的利用率已经很高,并行执行帮助不大
#通过以下设置开启并行执行
SET hive.exec.parallel=true; -- default false
SET hive.exec.parallel.thread.number=16; -- default 8,定义并行运行的最大数量
查询优化
- 自动启动Map端Join
- 防止数据倾斜
set hive.optimize.skewjoin=true;
- 启用CBO(Cost based Optimizer)
set hive.cbo.enable=true;
set hive.compute.query.using.stats=true;
set hive.stats.fetch.column.stats=true;//计算每个列的使用情况
set hive.stats.fetch.partition.stats=true;//分区的负载均衡
- 使用CTE、临时表、窗口函数等正确的编码约定
压缩算法
- 减少传输数据量,会极大提升MapReduce性能
采用数据压缩是减少数据量的很好的方式 - 常用压缩方法对比