一、什么是Hive?
通俗的说,hive就是一个MapReduce的翻译器,所以不用搭建一个集群。相对于HBase的主从架构,具有很多从节点regionServer,这样就把整个数据,分别分散到每个regionServer,进而实现整个大数据的查找,提高性能。而hive就是把sql语句翻译成MapReduce,然后再运行在yarn容器之上。
1、Hive基于HDFS之上的数据仓库
Hive | HDFS |
表 | 目录 |
数据 | 文件 |
分区 | 目录 |
桶 | 文件 |
这个对应关系要注意一下。
2、Hive是一个翻译器
默认:Hive on MapReduce,(SQL ----> MapReduce),还可以:Hive on Tez、 Hive on Spark (SQL ----> Spark任务)
3、支持HQL(SQL的一个子集)
二、体系架构
注意:由于元信息都要同步到MySQL,所以在配置的时候只需要配置jdbc信息就行。在用标准的jdbc程序的时候,需要启动hiveserver2
三、搭建Hive的环境
1、解压:
tar -zxvf apache-hive-3.1.2-bin.tar.gz -C ~/training/
2、设置环境变量 vi ~/.bash_profile
HIVE_HOME=/usr/local/hive/apache-hive
export HIVE_HOME
PATH=$HIVE_HOME/bin:$PATH
export PATH
生效source ~/.bash_profile
3、配置核心文件:conf/hive-site.xml
hive-site.xml这个文件并不存在,需要我们手动创建
3.1、嵌入模式:使用自带的Derby数据库存储元信息;只支持一个链接用于开发和测试
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<!--JDBC的URL-->
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:derby:;databaseName=metastore_db;create=true</value>
</property>
<!--JDBC的Driver-->
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>org.apache.derby.jdbc.EmbeddedDriver</value>
</property>
<!--使用本地内嵌的derby-->
<property>
<name>hive.metastore.local</name>
<value>true</value>
</property>
<!--嵌入模式下保存表中的数据-->
<property>
<name>hive.metastore.warehouse.dir</name>
<value>file:///root/training/apache-hive-3.1.2-bin/warehouse</value>
</property>
</configuration>
注意:在conf目录下,对Derby进行初始化
schematool -dbType derby -initSchema
启动Hive:在conf目录下,启动hive的客户端
3.2、本地模式、远程模式
配置之前,需要把mysql-connector-java-5.1.43-bin.jar包上传到$HIVE_HOME/lib目录下
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<!--JDBC的URL-->
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://192.168.92.130:3306/hive?useSSL=false</value>
</property>
<!--JDBC的Driver-->
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>root</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>123456</value>
</property>
</configuration>
初始化MySQL:初始化MetaStore:(可以在任何目录下进行)
schematool -dbType mysql -initSchema
这里可能出现MySQL数据没有权限,在MySQL授权操作如下:
创建用户操作
create user 'hiveOwner'@'%' identified by '123456';
1、删除匿名用户
mysql -uroot -p123456
然后,切换到数据库mysql。SQL如下:
mysql> use mysql;
然后,删除匿名用户。SQL如下:
mysql> delete from user where user='';
mysql> grant all privileges on *.* to 'root'@'%' identified by '123456' with grant option;
all orivileges 所有的权限
*.* :所有的数据库
'root'@'%' :root用户,也可以其他用户,在任何主机上运行,%可以换成主机地址。
初始化成功后,可以在MySQL上面看到建立了很多的表:
3.3、启动hive
启动之前,先把Hadoop启动
hive -S
四、(最重要)Hive的数据模型:支持的各种表
1、内部表
是Hive中最简单的表,保存的路径默认是:/user/hive/warehouse。实际工作中,很少用到。
例如:创建员工表
create table emp
(empno int,
ename string,
job string,
mgr int,
hiredate string,
sal int,
comm int,
deptno int);
注意:Hive的表,列与列的分隔符是一个不可见字符。
例如:指定数据之间的分隔符
row format delimited fields terminated by ‘,’
create table emp
(empno int,
ename string,
job string,
mgr int,
hiredate string,
sal int,
comm int,
deptno int)
row format delimited fields terminated by ',';
2、外部表
可以指向HDFS的任意的路径
例如:创建emp2
create external table emp2
(empno int,
ename string,
job string,
mgr int,
hiredate string,
sal int,
comm int,
deptno int)
row format delimited fields terminated by ','
location '/emp';
表对应的是路径。
外部表的实现就是把emp2与/emp路径做了一个映射,当访问emp2表时,就是去/emp路径下去找数据。
假如hdfs上有一个目录student,student目录下面有数据student.csv , student1.csv。
我们直接建立外部表create external table ex_student(),再通过location “/student”,让两者建立关系。这个表建立后,就有student目录下面的数据。
3、分区表
3.1、静态分区表:按部门号进行分区
partitioned by (deptno int)
create table emp_part
(empno int,
ename string,
job string,
mgr int,
hiredate string,
sal int,
comm int)
partitioned by (deptno int)
row format delimited fields terminated by ',';
插入数据(静态分区)
注意是怎么插入的!
insert into table emp_part partition(deptno=10)
select empno,ename,job,mgr,hiredate,sal,comm from emp1 where deptno=10;
insert into table emp_part partition(deptno=20)
select empno,ename,job,mgr,hiredate,sal,comm from emp1 where deptno=20;
3.2、动态分区表
启用Hive的动态分区:
set hive.exec.dynamic.partition =true;
默认false,表示是否开启动态分区功能
set hive.exec.dynamic.partition.mode = nonstrict;
默认strict,表示允许所有分区都是动态的,如果是strict必须有静态分区字段
(*)根据一个字段创建动态分区表:按照job建立动态分区
create table emp4
(empno int,ename string,sal int)
partitioned by (job string)
row format delimited fields terminated by ',';
插入数据:insert into table emp4 select empno,ename,sal,job from emp2;
我们可以观察到,建表是和静态建表是一样的,只不过插入数据的时候有所不同。静态插入数据是指定插入的分区,而动态不需要指定插入的分区。
(*)根据多个字段创建动态分区表:按照deptno,job建立动态分区
create table emp5
(empno int,ename string,sal int)
partitioned by (deptno int,job string)
row format delimited fields terminated by ',';
插入数据:insert into table emp5 select empno,ename,sal,deptno,job from emp2;
和插入一个字段道理一样,只不过在hdfs上面建立了多级目录,比如上面这个例子,在deptno目录下面还有job目录。
(*)结合静态分区和动态分区
create table emp6
(empno int,ename string,sal int)
partitioned by (deptno int,job string)
row format delimited fields terminated by ',';
deptno使用静态分区,job使用动态分区
插入数据:insert into table emp6 partition(deptno=10,job)
select empno,ename,sal,job from emp2 where deptno=10;
一个字段指定插入哪个分区,另一个不指定,动态生成。
4、桶表:Hash分区,每一个桶就是一个HDFS的文件
例如:根据员工的job建立4个桶
create table emp7
(empno int,
ename string,
job string,
mgr int,
hiredate string,
sal int,
comm int,
deptno int)
clustered by (job) into 4 buckets
row format delimited fields terminated by ',';
插入数据:insert into table emp7 select * from emp2;
桶表其实和分区差不多。桶表是插入数据的时候,根据字段的hash值进行存放在不同的文件里,而分区是根据目录进行划分的。
5、视图view
是一个“虚表”,作用:简化复杂的查询。虚:体现在视图不能缓存数据,所以视图不能提高性能
create view myview
as
select dept.dname,emp1.ename
from emp1,dept
where emp1.deptno=dept.deptno;
查询数据: select * from myview; 这条等价 上面的select语句。
视图有一点子查询的味道。那这条语句我也可以这样写:
select * from
(
select dept.dname,emp1.ename
from emp1,dept
where emp1.deptno=dept.deptno;
)
那么视图就像一个函数。
6、物化视图:从Hive的3.x开始支持
从Hive的3.x开始支持;本质就是一张表,只不过这张表存在缓存里面,提高查询的速度。
create materialized view myview1
as
select emp1.empno,emp1.ename,emp1.sal,dept.dname
from emp1,dept
where emp1.deptno=dept.deptno;
五、加载数据到Hive的表:insert语句、load语句、sqoop导入
1、load语句
- 加载HDFS的数据到员工表:
load data inpath '/scott/emp.csv' into table emp;
本质是:执行的是一个ctrl + x操作
- 加载本地Linux的数据到员工表
load data local inpath '/root/temp/emp.csv' into table emp1;
本质是:数据的上传
六、Hive的查询
支持HQL(Hive Query Language,是SQL的一个子集)
Hive的分析函数
1、 ROLL UP
通常与group by结合使用,实现group by语句增强
看个例子:
按部门、职位统计员工的工资总额、部门工资总额、所有员工的工资总额
select deptno,job,sum(sal) from emp group by deptno,job
union
select deptno,to_char(null),sum(sal) from emp group by deptno
union
select to_number(null),to_char(null),sum(sal) from emp;
就等于下面的一个语句
select deptno,job,sum(sal)
from emp1
group by rollup(deptno,job);
2、窗函数over
进行窗口计算,本质就是“分组”,跟group by的最大区别:over可以返回多个值
窗函数就是用来指定聚合函数(max、min、sum)的范围。并且每一行都要输出。指定要计算的范围
参数:
unbounded:指定哪些数据进入窗口
preceding:在...之前,用法:unbounded preceding:分组后的第一行
following:在...之后,用法:unbounded following:分组后的最后一行
current row:分组后的当前行
举例:
按部门进行分组,求:员工号,姓名,部门号,员工的薪水,每个部门的第一行到当前的行的工资总额
select empno,ename,deptno,sal,
sum(sal) over (partition by deptno order by sal
rows between unbounded preceding and current row)
from emp1;
rows可以换成range
3、row_number()、rank()和dense_rank()
补充:Oracle中也有行号:rownum(伪列)
row_number():在开窗函数基础上,表示行号
select empno,ename,deptno,sal,
row_number() over(partition by deptno order by sal)
from emp2;
rank():考虑了重复的记录,序号可能不连续
select empno,ename,deptno,sal,
rank() over(partition by deptno order by sal)
from emp1;
dense_rank():考虑了重复的记录,序号连续
select empno,ename,deptno,sal,
dense_rank() over(partition by deptno order by sal)
from emp1;
4、LAG, LEAD, FIRST_VALUE, LAST_VALUE
lag:滞后,把某个列往后错行
lead:提前,把某个列往前错行
select empno,ename,deptno,sal,
lag(sal,2) over(partition by deptno order by sal) 滞后,
lead(sal,2) over(partition by deptno order by sal) 提前
from emp1;
七、Hive的JDBC程序
1、修改hadoop 配置文件 etc/hadoop/core-site.xml
<property>
<name>hadoop.proxyuser.root.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.root.groups</name>
<value>*</value>
</property>
不修改会报错误:
User: root is not allowed to impersonate anonymous
2、从$HIVE_HOME/jdbc下载jar包,并添加到项目。
3、启动hiveserver2
4、Java代码如下:
- 创建JDBCUtils工具类
public class JDBCUtils {
private static String driver = "org.apache.hive.jdbc.HiveDriver";
private static String url ="jdbc:hive2://192.168.92.111:10000/default";
//注册驱动
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection(){
try {
return DriverManager.getConnection(url);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//释放连接
public static void release(Connection con , Statement st , ResultSet re){
if (con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}finally {
con = null;
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}finally {
st =null;
}
}
if (re != null){
try {
re.close();
} catch (SQLException e) {
e.printStackTrace();
}finally {
re = null;
}
}
}
}
- 创建TestConnectHive类测试连接
public class TestConnectHive {
public static void main(String[] args) {
Connection con = null;
Statement st = null;
ResultSet re = null;
String sql = "select * from emp2";
try {
con = JDBCUtils.getConnection();
st = con.createStatement();
re = st.executeQuery(sql);
while (re.next()){
//姓名和薪水
String ename = re.getString("ename");
double sal = re.getDouble("sal");
System.out.println(ename+"\t"+sal);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
八、Hive的自定义函数(相当于:存储过程、存储函数)
通俗的说,就是对输出的每一行的数据进行处理。
把所有的$HIVE_HOME/lib目录下面的jar包添加到工程下面
1、UDF:user define function 用户自定义函数
不改变表的结构进行输出,就是对每一行数据进行一个处理
输入单行,输出单行
举例:
- 把所有的$HIVE_HOME/lib目录下面的jar包添加到工程下面
- 编写Java代码两个例子
package com.wmh;
import org.apache.hadoop.hive.ql.exec.UDF;
//字符串连接
public class MyConcat extends UDF {
public String evaluate(String q1 , String q2){
return q1 + "**************" +q2;
}
}
public class CheckSalaryGrade extends UDF {
//根据工资输出等级
//这个方法就是调用这个函数传过来的值
public String evaluate(String salary){
int sal = Integer.parseInt(salary);
if(sal < 1000) return "Grade A";
else if(sal >= 1000 && sal < 3000) return "Grade B";
else return "Grade C";
}
}
- 打成jar,上传到Linux目录下。上传目录:/tools/mydefinefunction.jar
- 添加到hive环境中,执行下面命令
hive -S //启动hive
hive> delete jar /tools/mydefinefunction.jar;
hive> add jar /tools/mydefinefunction.jar; // 注册到hive环境中
//给Myconcat起一个别名为myconcat
hive> create temporary function myconcat as 'com.wmh.MyConcat';
2、UDTF:user define table-generation function 表的自定义函数
改变表的结构进行输出
输入单行,输出多行
过程同上。
import org.apache.hadoop.hive.ql.exec.UDFArgumentException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDTF;
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 jersey.repackaged.com.google.common.collect.Lists;
public class MyUDTF extends GenericUDTF {
@Override
public StructObjectInspector initialize(StructObjectInspector arg0) throws UDFArgumentException {
//得到原始数据的表结构
//列名
List<String> columnNames = Lists.newLinkedList();
columnNames.add("tid");
columnNames.add("key");
columnNames.add("value");
//得到原始数据的列的类型
List<ObjectInspector> columnTypes = Lists.newLinkedList();
columnTypes.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
columnTypes.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
columnTypes.add(PrimitiveObjectInspectorFactory.javaStringObjectInspector);
//返回原始数据的结构
return ObjectInspectorFactory.getStandardStructObjectInspector(columnNames, columnTypes);
}
@Override
public void process(Object[] args) throws HiveException {
// 读入的每条数据如何处理?
// 如何生成新的表?
if(args.length != 3) {
//非法数据
return;
}
//处理ID
String id = args[0].toString();
//处理key: name,age,gender
String[] keyList = args[1].toString().split(",");
//处理value:Tom,24,Male
String[] valueList = args[2].toString().split(",");
for(int i=0; i< keyList.length ; i++) {
//输出的记录: 1 name Tom
// 1 age 24
// 1 gender Male
String[] result = {id,keyList[i],valueList[i]};
this.forward(result);
}
}
@Override
public void close() throws HiveException {
// TODO Auto-generated method stub
}
}
3、UDAF:aggregate 聚合:sum、avg、等等
九、(了解)Hive on Tez
参看讲义!