前言
本章介绍hive内部表、外部表、分区表和UDF等。
1.hive表介绍
1.Hive虽说是数据仓库,其实可以认为就是一个mysql数据库,hive中的表名对应的是hdfs上的文件目录名,表内容就是对应目录下的文件。
2.hive表的hdfs路径
在hive-site.xml中,由参数hive.metastore.warehouse.dir指定,我的是/hivedata/warehouse。
2. 应用场景
1.每天将收集到的网站日志定期流入HDFS文本文件,一天一个目录;
2.在Hive中建立内部分区表,通过添加分区的方式,将每天HDFS上的原始日志映射到表的分区中;
3.在外部表(原始日志表)的基础上做大量的统计分析,用到的中间表、结果表使用内部表存储,数据通过SELECT+INSERT。
3.内部表
1.vim student.txt,并hadoop fs -put student.txt 上传到hdfs,举例(用’\t’间隔):
2.创建内部表指定分隔符:
create table student (id bigint,name string,age double,sex string) row format delimited fields terminated by ‘\t’;
3.从hdfs load数据到表中:
load data local inpath ‘hdfs://mycluster/test/student.txt’ into table student
4.查询数据:
注意:此时student.txt 被移走了!
4.外部表
1.创建外部表,关键字:external location
create external table student_ext (id bigint,name string,age double,sex string) row format delimited fields terminated by ‘\t’ location ‘/test/data/’。
2. /test/data/目录下所有数据都会被加载进来,但是hive表目录下没有新目录创建。
5.外部表和内部表区别
1.DROP时不同:内部表DROP时候会删除HDFS上的数据; 外部表DROP时候不会删除(类似于挂载的其他路径)HDFS上的数据;
2.适用场景不同:外部表需要定期将外部数据映射到表中,内部表一般作为中间表和结果表,比如外部表统计后的结果用内部表存储。
3.外部表要使用关键字 external 和 location(创建表可以不指定,增加分区需要)。
6.分区表
1.分区表其实就是数据量太大,将数据分不同目录来存储,统计时查询比较快。个人觉得分区表跟内部表和外部表没有什么关系,一定区分开来。
2.创建分区表
create table people (id bigint, name string) partitioned by (nation string) row format delimited fields terminated by ‘\t’;
3.load数据到分区表(会移走源文件)
load data inpath ‘hdfs://mycluster/test/data/china.txt’ into table people partition(nation=’China’)
load的方式会在hive中生成对应的分区目录,且目录下有数据。
4.直接增加分区,hive目录下不会产生分区目录。
alter table people5 add partition(nation=’China’) location ‘/China’;
5.说的再多不如自己一试,没接触过的可能觉得有点晕,自己试一下就知道了!
7.UDF
hive中,除了提供丰富的内置函数外,还允许用户自己定义UDF函数。
开发自定义UDF函数有三种方式:
1.继承org.apache.hadoop.hive.ql.exec.UDF;
2.继承org.apache.hadoop.hive.ql.udf.generic.GenericUDF;
3.通过脚本的方式,比如python UDF、scala UDF等。
简单的数据类型(比如String、Integer等)可以用UDF,复杂的数据类型(Array、Map、Struct)可以使用GenericUDF,或者通过脚本实现函数体。
比如,我们有个需求:消费2000元以上的评级为A,其他的评级为B,先来看下表中数据如下:
UDF实现
1.eclipse创建maven项目hive,引入依赖:
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cnepay</groupId>
<artifactId>hive</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>hive</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>1.1.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.hadoop/hadoop-common -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0</version>
<!-- <scope>provided</scope> -->
</dependency>
<dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.6</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
2.java代码
package com.cnepay.hive;
import org.apache.hadoop.hive.ql.exec.UDF;
import org.apache.hadoop.io.Text;
public class CountFee extends UDF{
private Text t = new Text();
public Text evaluate(Double fee){
if(fee >= 2000.0){
t.set("A");
}
else{
t.set("B");
}
return t;
}
}
3.将项目打成jar,并上传到Linux服务器
4.进入hive或beeline命令行,
a.将jar包加入到hive的classpath: add jar /home/hadoop/countFee.jar;
b.创建临时函数:create temporary function getFee as ‘com.cnepay.hive.CountFee’;
GenericUDF实现
1.继承org.apache.hadoop.hive.ql.udf.generic.GenericUDF之后,必须需要重写3个重要的方法:
public ObjectInspector initialize(ObjectInspector[] arguments)
//必选,该方法用于函数初始化操作,并定义函数的返回值类型;
public Object evaluate(DeferredObject[] args){}
//必选,函数处理的核心方法,用途和UDF中的evaluate一样;
public String getDisplayString(String[] children)
//必选,显示函数的帮助信息
2.java代码,关注下代码中注释
package com.cnepay.hive;
import java.util.ArrayList;
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
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.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.StructObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.primitive.PrimitiveObjectInspectorFactory;
import org.apache.hadoop.io.Text;
public class WxqGenericUDF extends GenericUDF{
private Object[] ret = new Object[2];
/**
* 作用:
* 1.检查输入类型是否正确;
* 2.初始化读取文件等操作;
* 3.定义函数的返回值类型是对象;
*
*/
@Override
public ObjectInspector initialize(ObjectInspector[] arguments)
throws UDFArgumentException {
//检查输入参数个数
if(arguments.length != 2){
throw new UDFArgumentException(" The function 'WxqGenericUDF' accepts 2 argument !");
}
//存储struct对象的属性名称
ArrayList<String> structFieldNames = new ArrayList<>();
//存储struct对象的属性值的类型
ArrayList<ObjectInspector> structFieldObjectInspectors = new ArrayList<>();
//创建两个属性,name 和 level
structFieldNames.add("name");
structFieldNames.add("level");
//定义name和level值的类型为string
structFieldObjectInspectors.add(PrimitiveObjectInspectorFactory.writableStringObjectInspector);
structFieldObjectInspectors.add(PrimitiveObjectInspectorFactory.writableStringObjectInspector);
//定义函数的返回类型是struct对象
StructObjectInspector soi = ObjectInspectorFactory.getStandardStructObjectInspector(structFieldNames, structFieldObjectInspectors);
return soi;
}
/**
* 遍历每条记录
*/
@Override
public Object evaluate(DeferredObject[] arguments) throws HiveException {
String name = arguments[0].get().toString();
Double fee = Double.parseDouble(arguments[1].get().toString());
//编写具体业务逻辑,封装返回对象。
ret[0] = new Text(name);
if(fee >= 2000.0){
ret[1] = new Text("A");
}
else{
ret[1] = new Text("B");
}
return ret;
}
@Override
public String getDisplayString(String[] children) {
// TODO Auto-generated method stub
return "Usage: WxqGenericUDF(String str1, Double d)";
}
}