一、Hive简介
Hive是Facebook开发的构建于Hadoop集群之上的数据仓库应用,可以将结构化的数据文件映射为一张数据库表,并提供完整的SQL查询功能,可以将SQL语句转换为MapReduce任务进行运行。
Hive是一个可以提供有效的、合理的且直观的组织和使用数据的模型,即使对于经验丰富的Java开发工程师来说,将这些常见的数据运算对应到底层的MapReduce Java API也是令人敬畏的。Hive可以帮用户做这些工作,用户就可以集中精力关注查询本身了。Hive可以将大多数的查询转换为MapReduce任务。Hive最适合于数据仓库应用程序,使用该应用程序进行相关的静态数据分析,不需要快速响应给出结果,而且数据本身也不会频繁变化。
Hive不是一个完整的数据库。Hadoop以及HDFS的设计本身约束和局限性限制了Hive所能胜任的工作。最大的限制就是Hive不支持记录级别的更新、插入或者删除。用户可以通过查询生成新表或将查询结果导入到文件中去。因为,Hadoop是一个面向批处理的系统,而MapReduce启动任务启动过程需要消耗很长时间,所以Hive延时也比较长。Hive还不支持事务。因此,Hive不支持联机事务处理(OLTP),更接近于一个联机分析技术(OLAP)工具,但是,目前还没有满足“联机”部分。
Hive提供了一系列的工具,可以用来进行数据提取转化加载(ETL),其中,ETL是一种可以存储、查询和分析存储在Hadoop中的大规模数据的机制。因此,Hive是最适合数据仓库应用程序的,它可以维护海量数据,而且可以对数据进行挖掘,然后形成意见和报告等。
因为大多数的数据仓库应用程序是基于SQL的关系数据库现实的,所以,Hive降低了将这些应用程序移植到Hadoop上的障碍。如果用户懂得SQL,那么学习使用Hive会很容易。因为Hive定义了简单的类SQL 查询语言——HiveQL,这里值得一提的是,与SQLServer、Oracle相比,HiveQL和MySQL提供的SQL语言更接近。同样的,相对于其他的Hadoop语言和工具来说,Hive也使得开发者将基于SQL的应用程序移植到Hadoop变得更加容易。
二、Hive的常用HiveQL操作
(一)Hive基本数据类型
首先,我们简单叙述一下HiveQL的基本数据类型。
Hive支持基本数据类型和复杂类型, 基本数据类型主要有数值类型(INT、FLOAT、DOUBLE ) 、布尔型和字符串, 复杂类型有三种:ARRAY、MAP 和 STRUCT。
1.基本数据类型
TINYINT: 1个字节
SMALLINT: 2个字节
INT: 4个字节
BIGINT: 8个字节
BOOLEAN: TRUE/FALSE
FLOAT: 4个字节,单精度浮点型
DOUBLE: 8个字节,双精度浮点型STRING 字符串
2.复杂数据类型
ARRAY: 有序字段
MAP: 无序字段
STRUCT: 一组命名的字段
(二)常用的HiveQL操作命令
Hive常用的HiveQL操作命令主要包括:数据定义、数据操作。接下来详细介绍一下这些命令即用法(想要了解更多请参照《Hive编程指南》一书)。
1. 数据定义
主要用于创建修改和删除数据库、表、视图、函数和索引。
(1)创建、修改和删除数据库
$ create database if not exists hive;#创建数据库
$ show databases;#查看Hive中包含数据库
$ show databases like 'h.*'#查看Hive中以h开头数据库
$ describe databases; #查看hive数据库位置等信息
$ alter database hive set dbproperties; #为hive设置键值对属性
$ use hive; #切换到hive数据库下
$ drop database if exists hive; #删除不含表的数据库
$ drop database if exists hive cascade; #删除数据库和它中的表
注意:除 dbproperties属性外, 数据库的元数据信息都是不可更改的,包括数据库名和数据库所在的目录位置,没有办法删除或重置数据库属性。
(2)创建、修改和删除表
$ create table if not exists hive.usr( #创建内部表(管理表)
name string comment 'username',
pwd string comment 'password',
address struct<street:string,city:string,state:string,zip:int>,
comment 'home address',
identify map<int,tinyint> comment 'number,sex')
comment 'description of the table'
tblproperties('creator'='me','time'='2016.1.1');
$ create external table if not exists usr2( #创建外部表
name string,
pwd string,
address struct<street:string,city:string,state:string,zip:int>,
identify map<int,tinyint>)
row format delimited fields terminated by ','
location '/usr/local/hive/warehouse/hive.db/usr';
$ create table if not exists usr3(#创建分区表
name string,
pwd string,
address struct<street:string,city:string,state:string,zip:int>,
identify map<int,tinyint>)
partitioned by(city string,state string);
$ create table if not exists hive.usr1 like hive.usr;#复制usr表的表模式
$ show tables in hive; #查看hive数据库中所有表
$ show tables 'u.*'; #查看hive中以u开头的表
$ describe hive.usr; #查看usr表相关信息
$ alter table usr rename to custom; #重命名表
$ alter table usr2 add if not exists #为表增加一个分区
partition(city=”beijing”,state=”China”)
location '/usr/local/hive/warehouse/usr2/China/beijing';
$ alter table usr2 partition(city=”beijing”,state=”China”)#修改分区路径
set location '/usr/local/hive/warehouse/usr2/CH/beijing';
$ alter table usr2 drop if exists partition(city=”beijing”,state=”China”) #删除分区
$ alter table usr change column pwd password string after address; #修改列信息
$ alter table usr add columns(hobby string);#增加列
$ alter table usr replace columns(uname string); #删除替换列
$ alter table usr set tblproperties('creator'='liming'); #修改表属性
$ alter table usr2 partition(city=”beijing”,state=”China”)#修改存储属性
set fileformat sequencefile;
$ use hive; #切换到hive数据库下
$ drop table if exists usr1; #删除表
$ drop database if exists hive cascade; #删除数据库和它中的表
(3)视图和索引的创建、修改和删除
主要语法如下,用户可自行实现。
$ create view view_name as....; #创建视图
$ alter view view_name set tblproperties(…); #修改视图
因为视图是只读的,所以 对于视图只允许改变元数据中的 tblproperties属性。
$ drop view if exists view_name; #删除视图
$ create index index_name on table table_name(partition_name/column_name) #创建索引
as 'org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler' with deferred rebuild....;
这里'org.apache.hadoop.hive.ql.index.compact.CompactIndexHandler'是一个索引处理器,即一个实现了索引接口的Java类,另外Hive还有其他的索引实现。
$ alter index index_name on table table_name partition(...) rebulid;#重建索引如果使用 deferred rebuild,那么新索引成空白状态,任何时候可以进行第一次索引创建或重建。
$ show formatted index on table_name; #显示索引
$ drop index if exists index_name on table table_name; #删除索引
(4)用户自定义函数
在新建用户自定义函数(UDF)方法前,先了解一下Hive自带的那些函数。“show functions;”命令会显示Hive中所有的函数名称:
若想要查看具体函数使用方法可使用describe function 函数名;
首先编写自己的UDF前需要继承UDF类并实现evaluate()函数,或是继承GenericUDF类实现initialize()函数、evaluate()函数和getDisplayString()函数,还有其他的实现方法,感兴趣的用户可以自行学习。
另外,如果用户想在Hive中使用该UDF需要将我们编写的Java代码进行编译,然后将编译后的UDF二进制类文件(.class文件)打包成一个JAR文件,然后在Hive会话中将这个JAR文件加入到类路径下,在通过create function语句定义好使用这个Java类的函数。
$ add jar <jar文件的绝对路径>; #创建函数
$ create temporary function function_name;
$ drop temporary function if exists function_name; #删除函数
2.数据操作
主要实现的是将数据装载到表中(或是从表中导出),并进行相应查询操作,对熟悉SQL语言的用户应该不会陌生。
(1)向表中装载数据
这里我们以只有两个属性的简单表为例来介绍。首先创建表stu和course,stu有两个属性id与name,course有两个属性cid与sid。
$ create table if not exists hive.stu(id int,name string)
row format delimited fields terminated by '\t';
$ create table if not exists hive.course(cid int,sid int)
row format delimited fields terminated by '\t';
向表中装载数据有两种方法:从文件中导入和通过查询语句插入。
a.从文件中导入
假如这个表中的记录存储于文件stu.txt中,该文件的存储路径为usr/local/hadoop/examples/stu.txt,内容如下,
stu.txt:
1 xiapi
2 xiaoxue
3 qingqing
下面我们把这个文件中的数据装载到表stu中,操作如下:
$ load data local inpath '/usr/local/hadoop/examples/stu.txt' overwrite into table stu;
如果stu.txt文件存储在HDFS 上,则不需要 local 关键字。
b.通过查询语句插入
使用如下命令,创建stu1表,它和stu表属性相同,我们要把从stu表中查询得到的数据插入到stu1中:
$ create table stu1 as select id,name from stu;
上面是创建表,并直接向新表插入数据;若表已经存在,向表中插入数据需执行以下命令:
$ insert overwrite table stu1 select id,name from stu where(条件);
这里关键字overwrite的作用是替换掉表(或分区)中原有数据,换成into关键字,直接追加到原有内容后。
(2)从表中导出数据
a.可以简单拷贝文件或文件夹
命令如下:
$ hadoop fs -cp source_path target_path;
b.写入临时文件
命令如下:
$ insert overwrite local directory '/usr/local/hadoop/tmp/stu' select id,name from stu;
(3)查询操作
和SQL的查询完全一样,这里不再赘述。主要使用select...from...where...等语句,再结合关键字group by、having、like、rlike等操作。这里我们简单介绍一下SQL中没有的case...when...then...句式、join操作和子查询操作。
case...when...then...句式和if条件语句类似,用于处理单个列的查询结果,语句如下:
$ select id,name,
case
when id=1 then 'first'
when id=2 then 'second'
else 'third'
end from stu;
结果如下:
连接(join)是将两个表中在共同数据项上相互匹配的那些行合并起来, HiveQL 的连接分为内连接、左向外连接、右向外连接、全外连接和半连接 5 种。
a. 内连接(等值连接)
内连接使用比较运算符根据每个表共有的列的值匹配两个表中的行。
首先,我们先把以下内容插入到course表中(自行完成)。
1 3
2 1
3 1
下面, 查询stu和course表中学号相同的所有行,命令如下:
$ select stu.*, course.* from stu join course on(stu .id=course .sid);
执行结果如下:
b. 左连接
左连接的结果集包括“LEFT OUTER”子句中指定的左表的所有行, 而不仅仅是连接列所匹配的行。如果左表的某行在右表中没有匹配行, 则在相关联的结果集中右表的所有选择列均为空值,命令如下:
$ select stu.*, course.* from stu left outer join course on(stu .id=course .sid);
执行结果如下:
c. 右连接
右连接是左向外连接的反向连接,将返回右表的所有行。如果右表的某行在左表中没有匹配行,则将为左表返回空值。命令如下:
$ select stu.*, course.* from stu right outer join course on(stu .id=course .sid);
执行结果如下:
d. 全连接
全连接返回左表和右表中的所有行。当某行在另一表中没有匹配行时,则另一个表的选择列表包含空值。如果表之间有匹配行,则整个结果集包含基表的数据值。命令如下:
$ select stu.*, course.* from stu full outer join course on(stu .id=course .sid);
执行结果如下:
e. 半连接
半连接是 Hive 所特有的, Hive 不支持 in 操作,但是拥有替代的方案; left semi join, 称为半连接, 需要注意的是连接的表不能在查询的列中,只能出现在 on 子句中。命令如下:
$ select stu.* from stu left semi join course on(stu .id=course .sid);
执行结果如下:
(4)子查询
标准 SQL 的子查询支持嵌套的 select 子句,HiveQL 对子查询的支持很有限,只能在from 引导的子句中出现子查询。
注意:在定义或是操作表时,不要忘记指定所需数据库。
三、 Hive简单编程实践
下面我们以词频统计算法为例,来介绍怎么在具体应用中使用Hive。词频统计算法又是最能体现MapReduce思想的算法之一,这里我们可以对比它在MapReduce中的实现,来说明使用Hive后的优势。
MapReduce实现词频统计的代码可以通过下载Hadoop源码后,在$ HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.1.jar包中找到(wordcount类),wordcount类由63行Java代码编写而成。下面首先简单介绍一下怎么使用MapReduce中wordcount类来统计单词出现的次数,具体步骤如下:
(1)创建input目录,output目录会自动生成。其中input为输入目录,output目录为输出目录。命令如下:
$ cd /usr/local/hadoop
$ mkdir input
(2)然后,在input文件夹中创建两个测试文件file1.txt和file2.txt,命令如下:
$ cd /usr/local/hadoop/input
$ echo "hello world" > file1.txt
$ echo "hello hadoop" > file2.txt
(3)执行如下hadoop命令:
$ cd ..
$ hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.4.1.jar wordcount input output
(4)我们可以到output文件夹中查看结果,结果如下:
下面我们通过HiveQL实现词频统计功能,此时只要编写下面7行代码,而且不需要进行编译生成jar来执行。HiveQL实现命令如下:
$ create table docs(line string);
$ load data inpath 'input' overwrite into table docs;
$ create table word_count as
select word, count(1) as count from
(select explode(split(line,' '))as word from docs) w
group by word
order by word;
执行后,用select语句查看,结果如下:
由上可知,采用Hive实现最大的优势是,对于非程序员,不用学习编写Java MapReduce代码了,只需要用户学习使用HiveQL就可以了,而这对于有SQL基础的用户而言是非常容易的。