涉及到的知识点:
- Hive的体系结构
- 安装和配置
- Hive的数据模型:内部表、分区表、外部表,桶表、视图
- Hive的查询(本质就是SQL)
- Hive的Java API(本质就是JDBC程序)
- Hive的自定义函数(UDF:user defined function。本质就是一个Java程序)
Hive的体系结构
Hive其实是构建在Hadoop上的数据仓库平台,为数据仓库管理提供了许多功能。其中最常用的功能就是翻译器,将我们的SQL语句,通过Hive引擎翻译称为MapReduce程序。不过需要注意的是Hive支持的是SQL99标准的一个子集,并不是全部支持。下面是Hive的体系结构图:
在这里顺便提一下Hive和HDFS的对应关系(后面会说到):
Hive HDFS
表 –> 目录
分区 –> 目录
数据 –> 文件
桶 –> 文件
Hive的安装和配置
先安装并设置环境变量,后面再来说几种配置模式吧。
安装命令如下:
tar -zxvf apache-hive-2.3.0-bin.tar.gz -C ~/training/
安装完成之后就是环境变量的设置:
//编辑环境变量配置文件
vi ~/.bash_profile
HIVE_HOME=/root/training/apache-hive-2.3.0-bin
export HIVE_HOME
PATH=$HIVE_HOME/bin:$PATH
export PATH
//使环境变量生效
source ~/.bash_profile
设置完成之后在命令行输入hive,然后按两下Tab键,产生如下结果说明配置成功:
接下来就是配置Hive的三种模式:
(1)嵌入模式
(2)本地模式
(3)远程模式
这三种模式都是通过配置文件: conf/hive-site.xml来实现的,需要注意的是hive-site.xml并不存在,我们需要自己新建。
嵌入模式
特点:
- 不需要MySQL的支持,使用Hive的自带的数据库Derby
- 局限:只支持一个连接
hive-site.xml具体配置如下:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:derby:;databaseName=metastore_db;create=true</value>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>org.apache.derby.jdbc.EmbeddedDriver</value>
</property>
<property>
<name>hive.metastore.local</name>
<value>true</value>
</property>
<!-- value对应的目录要事先存在 -->
<property>
<name>hive.metastore.warehouse.dir</name>
<value>file:///root/training/apache-hive-2.3.0-bin/warehouse</value>
</property>
</configuration>
配置好hive-site.xml,我们需要初始化一下Derby数据库,我们在Hive的体系结构中提到,需要一个关系型数据库来保存Hive的元信息,在嵌入模式中使用自带的Derby即可,但是需要初始化,命令如下:
初始化Derby数据库
schematool -dbType derby -initSchema
初始化成功的标志如下:
下面在命令行输入hive等待即可,如果顺利进入hive命令行模式说明嵌入式配置成功,结果如下:
现在我们来创建一个table1,并插入一条数据,查看一下效果:
显然已经成功,需要注意的是表的这些信息就是保存在我们上面配置的那个warehouse目录下,并不是在HDFS,我们可以去查看一下:
远程模式、本地模式
在进行远程模式、本地模式的配置之前,我们需要安装配置一下MySQL,具体步骤如下:
(1)解压压缩包
tar -xvf mysql-5.7.19-1.el7.x86_64.rpm-bundle.tar
(2)安装并配置MySQL:
//先删除linux默认安装的mysql驱动
yum remove mysql-libs
rpm -ivh mysql-community-common-5.7.19-1.el7.x86_64.rpm
rpm -ivh mysql-community-libs-5.7.19-1.el7.x86_64.rpm
rpm -ivh mysql-community-client-5.7.19-1.el7.x86_64.rpm
rpm -ivh mysql-community-server-5.7.19-1.el7.x86_64.rpm
rpm -ivh mysql-community-devel-5.7.19-1.el7.x86_64.rpm (可选)
启动MySQL:service mysqld start
或者:systemctl start mysqld.service
查看root用户的密码:cat /var/log/mysqld.log | grep password
登陆:mysql -uroot -p
修改密码:alter user 'root'@'localhost' identified by 'Welcome_1';
MySQL数据库的配置:
创建一个新的数据库:create database hive;
创建一个新的用户:
create user 'hiveowner'@'%' identified by 'Welcome_1';
给该用户授权
grant all on hive.* TO 'hiveowner'@'%';
grant all on hive.* TO 'hiveowner'@'localhost' identified by 'Welcome_1';
(3)安装MySQL的客户端 mysql front(免费的,百度到官网自己下即可)
新建链接,配置如下:
填写完之后点击OK,然后选中点击Open,就可见到如下页面:
好了,所有的MySQL相关的都已经安装配置完成了。下面我们进入Hive正式的配置:
(1)hive-site.xml文件(其实就是JDBC相关的配置):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
<property>
<name>javax.jdo.option.ConnectionURL</name>
<value>jdbc:mysql://localhost:3306/hive?useSSL=false</value>
</property>
<property>
<name>javax.jdo.option.ConnectionDriverName</name>
<value>com.mysql.jdbc.Driver</value>
</property>
<property>
<name>javax.jdo.option.ConnectionUserName</name>
<value>hiveowner</value>
</property>
<property>
<name>javax.jdo.option.ConnectionPassword</name>
<value>Welcome_1</value>
</property>
</configuration>
(2)将mysql的jar包放到lib目录下,注意一定要使用高版本的MySQL驱动(5.1.43以上的版本)
(3)初始化MySQL数据库
(*)老版本:当第一次启动Hive的时候 自动进行初始化
(*)新版本:schematool -dbType mysql -initSchema
初始化成功日志如下:
现在我们再通过MySQL-Front看一下hive数据库:
可以看见mysql生成了一系列的表用来保存hive的元信息。
到这里关系环境的配置就都完成了,后面我们开始介绍hive的一些常用知识点。
Hive的数据模型(注意:默认列的分隔符是tab键(制表符))
了解数据模型的目的是可以根据我们的需求结合各种数据模型,选择出最合适的来创建,方便我们后期工作的开展。
测试数据为员工表和部分表:
7499,ALLEN,SALESMAN,7698,1981/2/20,1600,300,30(员工表中的一条)
内部表:相当于MySQL的表
我们以上面员工表中一条为模板来创建内部表:
create table emp(
empno int,
ename string,
job string,
mgr int,
hiredata string,
sal int,
comm int,
deptno int
);
创建结果如下:
创建成功之后我们可以去看一下,在前面我们提到的关于hive的元信息,比如表名、列名等,我们通过MySQL-Front
在体系结构的最后我们提到了,Hive的表对应HDFS下的目录,我们现在可以通过web控制台去查看,地址ip:50070:
HDFS控制台:192.168.171.113:50070
创建表完成之后,我们来导入数据,有以下两种方式:
- insert(平常用的插入一条数据)
- load(从某个HDFS的目录或者本地Linux的目录上,把数据导入Hive的表 本质ctrl+x)
load data inpath ‘/input/emp.csv’ into table emp1; inpath表示导入HDFS的数据
load data local inpath ‘/root/temp/emp.csv’ into table emp1; local inpath表示导入本地Linux的数据
我们执行load data inpath ‘/input/emp.csv’ into table emp1; 完成之后hdfs目录下/input下的emp.csv就不在了,因为被剪切走了,我们看一下:
既然已经load成功,我们就来查询一下:
这是什么鬼,难道我们那里操作失误了。答案是:我们确实失误了,因为所有Hive的数据模型,列的分隔符默认是Tab,而我们的emp.csv却是以,分割的,所以查询出来的数据异常,下面我们重新来创建表emp2,并制定分隔符为”,”,然后重新导入再来查看一下结果:
(1)创建新表emp2,并指定lie分隔符
create table emp2
(empno int,
ename string,
job string,
mgr int,
hiredate string,
sal int,
comm int,
deptno int)
row format delimited fields terminated by ',';
(2)从linux本地导入数据
load data local inpath '/root/temp/emp.csv' into table emp2;
(3)查询结果如下:
分区表
其实我们在MapperReduce的使用及高级功能的分区功能中已经提到过分区的概念,不了解的建议花点时间去看一下。Hive创建分区表的步骤如下:
(1)根据员工的部门号创建分区
create table emp_part
(empno int,
ename string,
job string,
mgr int,show
hiredate string,
sal int,
comm int)
partitioned by (deptno int) -->(部门号单独提取出来,用于作为分区条件)
row format delimited fields terminated by ',';
(2)指明导入的数据的分区(通过子查询导入数据) —-> MapReduce程序
insert into table emp_part partition(deptno=10) select empno,ename,job,mgr,hiredate,sal,comm from emp2 where deptno=10;
insert into table emp_part partition(deptno=20) select empno,ename,job,mgr,hiredate,sal,comm from emp2 where deptno=20;
insert into table emp_part partition(deptno=30) select empno,ename,job,mgr,hiredate,sal,comm from emp2 where deptno=30;
我们在执行上面的插入数据时发现,每次执行一条语句默认会打印出很多日志,此时不利于我们的操作和观察,我们可以开启hive的静默模式,操作如下:
//先离开hive命令行模式
quit;
//重新启动
hive -S --> 报错不会受到影响
上两张图对比一下开启前后的效果:
很明显,感觉爽多了。我们继续上面的正题。现在我们分区表已经初始化完成,我们分别通过内部表(普通表)和分区表来查询一下deptno=10的数据:
结果是一样的,那么分区表存在的意义是什么呢?答案是效率更高,我们通过查询SQL的执行计划来作一下对比:
前面为内部表(普通表),后面为分区表,分区表明显查询数据的大小小了很多,在效率上有显著的提升。这就是分区表的用处。
执行计划如何看?从下往上,从右往左
视图
视图其实是一个虚表,具有以下的特点:
- 视图不存数据,依赖于其它的表,被视图依赖的表叫基表
- 操作视图跟操作表 一样。原因其实就是第一条,它本身不存数据,而是依赖于其它基表
- 视图并不可以提高查询的效率,它的作用是简化复杂的查询
- 物化视图(可以缓存数据)–> 可以自己去研究一下
举例查询员工信息:部门名称 员工姓名
(1)创建部门表并导入数据
create table dept
(deptno int,
dname string,
loc string)
row format delimited fields terminated by ',';
然后导入数据:
load data inpath ‘/input/dept.csv’ into table dept;(linux本地导入)
(2)创建视图
create view myview
as
select dept.dname,emp2.ename
from emp2,dept
where emp2.deptno=dept.deptno;
(3)查询结果
select * from myview;
外部表
外部表和内部表在元数据的组织上是相同的,而实际数据的存储则有较大的差异,它是指向HDFS上已经存在的数据,可以创建Partition。除此之外还具有以下特点:
- 只有一个过程,加载数据和创建表同时完成,并不会移动到数据仓库目录中,只是与外部数据建立一个链接。当删除一个外部表时,仅删除该链接
介绍完定义,我们下面来作一波操作演示一下外部表的使用:
(1)创建两个测试数据(linux本地)
//student01.txt
vi student01.txt
1,Tom,21
2,Jane,22
//student02.txt
3,Anna,23
(2)在HDFS上创建student目录,并把上面的测试数据上传上去
//在HDFS上创建student目录
hdfs dfs -mkdir /students
//上传数据到HDFS
hdfs dfs -put student01.txt /students
hdfs dfs -put student02.txt /students
(3)创建外部表,关联HDFS上的student目录
create external table students_ext --> 关键字external用于标识这是一个外部表
(sid int,sname string,age int)
row format delimited fields terminated by ','
location '/students'; --> 关键字location用于指定HDFS上的目录
(4)hive查看结果:
(5)在Linux本地新建student03.txt,添加一条数据,然后上传到HDFS /students目录下,再次查看Hive:
桶表:桶是一个文件
桶表是对数据进行哈希取值,然后放到不同文件中存储。其思想和我们前面的文章MapperReduce的使用及高级功能 里面提到的Hash分区是一样的,如果不了解的可以去看一下。
下面以创建一个桶表为例:
(1)创建桶表
create table emp_bucket
(empno int,
ename string,
job string,
mgr int,
hiredate string,
sal int,
comm int,
deptno int)
clustered by (job) into 4 buckets --> 关键字clustered标识桶表
row format delimited fields terminated by ',';
(2)设置环境变量打开桶表,否则无效
set hive.enforce.bucketing = true;
(3)通过子查询插入数据
insert into emp_bucket select * from emp2;
(4)查询结果
Hive的查询
Hive的查询本质就是SQL,通过输入sql语句,将其转化为MapReduce操作(简单的并不会转换),最终得到结果。我们列几个示例介绍一下即可。
(1)查询所有的员工信息
select * from emp2;
(2)查询员工信息:员工号 姓名 薪水
select empno,ename,sal from emp2;
(3)多表查询:部门名称 员工姓名
select dept.dname,emp2.ename
from emp2,dept
where emp2.deptno=dept.deptno;
(4)子查询hive只支持:from和where子句中的子查询
select * from emp2
where emp2.deptno in (select deptno from dept where dname='SALES');
补充:Oracle数据库中可以使用子查询的:from、where、select、having后面
不能使用子查询:group by
(5)条件函数: case …. when ….(Oracle中叫条件表达式)
是标准的SQL语句
实际上就是在SQL中实现一个if else 逻辑
举例:做报表,根据职位给员工涨工资
PRESIDENT 1000
MANAGER 800
其他 400
把涨前、涨后的薪水显示出来
select empno,ename,job,sal,
case job when 'PRESIDENT' then sal+1000
when 'MANAGER' then sal+800
else sal+400
end
from emp2;
补充:Oracle中,条件表达式还有一种方式:decode函数 —> Oracle自己的语法
通过JDBC操作Hive
在通过JDBC操作Hive时,有以下几点需要注意:
(1)jar包,把hive下的jar包拷贝一份进入项目,具体目录如下:
/root/training/apache-hive-2.3.0-bin/lib (只要jar包即可)
(2)启动HiveServer2(这个我们在最开始介绍Hive的体系结构的时候已经说明),命令如下:
hiveserver2 &(不是hive命令行模式,而是正常linux命令行模式)
(3)修改hadoop的配置文件 – core-site.xml,否则会报以下错误:
Caused by: java.lang.RuntimeException: org.apache.hadoop.ipc.RemoteException:User: root is not allowed to impersonate anonymous
具体配置如下:
<property>
<name>hadoop.proxyuser.root.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.root.groups</name>
<value>*</value>
</property>
配置完成之后将hadoop重启,然后运行代码,结果如下:
下面把代码贴上,需要的直接复制,黏贴即可:
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* function:工具类
* 1、获取链接
* 2、释放资源:Connection、Statement、ResultSet
* author by cpMark
* create on 2018/5/11.
*/
public class JDBCUtils {
/**
* Hive的驱动(固定写法)
*/
private static String mDriver = "org.apache.hive.jdbc.HiveDriver";
//Oracle数据库: oracle.jdbc.OracleDriver
/**
* Hive的URL地址
*/
private static String mUrl = "jdbc:hive2://192.168.171.113:10000/default";
/**
* 注册数据库的驱动
*/
static{
try{
Class.forName(mDriver);
}catch(Exception ex){
throw new ExceptionInInitializerError(ex);
}
}
/**
* 获取Hive数据库的链接
* @return
*/
public static Connection getConnection(){
try{
return DriverManager.getConnection(mUrl);
}catch(Exception ex){
ex.printStackTrace();
}
return null;
}
/**
* 释放资源
* @param conn
* @param st
* @param rs
*/
public static void release(Connection conn, Statement st, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
rs = null;
}
}
if(st != null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
st = null;
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
conn = null;
}
}
}
}
package jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* function:
* author by cpMark
* create on 2018/5/11.
*/
public class HiveMain {
public static void main(String[] args){
//查询员工信息
String sql = "select * from emp2";
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JDBCUtils.getConnection();
//得到SQL的运行环境
st = conn.createStatement();
//运行SQL
rs = st.executeQuery(sql);
while (rs.next()){
//姓名和薪水
//注意:好像不能通过列的索引号获取。只能通过列名来获取
String ename = rs.getString("ename");
double sal = rs.getDouble("sal");
System.out.println(ename+"\t"+sal);
}
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.release(conn,st,rs);
}
}
}
Hive的自定义函数
Hive的自定义函数(UDF:user defined function):本质就是一个Java程序,只是封装了我们自己的业务逻辑。下面我们以一个例子来演示如何使用自定义函数 – concat函数,拼接字符串:
package udf;
import org.apache.hadoop.hive.ql.exec.UDF;
/**
* function:拼接字符串的功能
* author by cpMark
* create on 2018/5/12.
*/
public class MyConcatString extends UDF {
/**
* 定义的方法,方法名必须叫:evaluate
*/
public String evaluate(String a,String b){
return a + "***" + b;
}
}
导出成jar包,并部署(部署命令在下图),结果如下:
到此为止关于Hive的所有知识点都已经介绍完了。下一篇数据分析引擎Pig,敬请期待!!!