Hive是基于Hadoop的一个数据仓库工具。可以将结构化数据映射为一张数据库表。并提供类似SQL的HiveSQL(HQL)进行数据查询等功能。Hive底层将HQL转换为MapReduce任务来操作HDFS中的数据。
利用Hive,可以快速实现MapReduce功能。而不必编写MapReduce程序。
如果不会编写Java程序,又想使用MapReduce来处理大数据,就可以使用Hive。
注意Hive不能作为一般数据库使用(无事务,有大量自建冗余),也不适合实时数据分析,无法进行行级别的增删改(受Hadoop限制),是一款离线数据分析工具。
Hive下载的时候记得要选择和Hadoop版本符合的版本。
Hive安装前必须保证机器上已经提前安装了jdk和Hadoop。并且配置好了环境变量(JAVA_HOME, HADOOP_HOME)。在下面的例子中,我的机器中已经安装了Hadoop 2.7.1伪分布式环境和jdk 1.8_65。
我将会在CentOS6.5中安装hive 1.2.0作为示例。解压hive的tar包:
[root@hadoop1 ~]# tar xzf apache-hive-1.2.0-bin.tar.gz -C /usr/develop/
[root@hadoop1 ~]# cd /usr/develop/
[root@hadoop1 develop]# mv apache-hive-1.2.0-bin/ hive1.2
如果你的系统中有”HADOOP_HOME”这个环境变,Hive就可以直接使用了。,如果没有,可以到/etc/profile下增加这个环境变量。
通过hive目录下的bin中的hive程序就可以直接执行Hive了:
[root@hadoop1 develop]# cd hive1.2/
[root@hadoop1 hive1.2]# ls
bin conf examples hcatalog lib LICENSE NOTICE README.txt RELEASE_NOTES.txt scripts
[root@hadoop1 hive1.2]# cd bin/
[root@hadoop1 bin]# ls
beeline ext hive hive-config.sh hiveserver2 metatool schematool
[root@hadoop1 bin]# ./hive
Logging initialized using configuration in jar:file:/usr/develop/hive1.2/lib/hive-common-1.2.0.jar!/hive-log4j.properties
hive>
这就直接进入了Hive的命令行界面,表示安装成功了。
HiveSQL的基本使用和标准SQL很像。支持:
HiveSQL创建表的命令也和sql很像:
Hive可以使用的类型:
Hive类型 | java类型 |
---|---|
TINYINT | byte |
SMALLINT | short |
INT | int |
BIGINT | long |
BOOLEAN | boolean |
FLOAT | float |
DOUBLE | double |
STRING | String |
TIMESTAMP | Timestamp |
BINARY | byte[] |
后面的row format…的作用是指定数据源的分隔符。这在从外部导入数据的时候特别有用。
Hive创建database实际上就是在HDFS上创建一个/user/hive/warehouse/xxx.db的目录。在database中创建table实际上就是在这个目录中创建一个和表名一样的目录。目录中储存了各种数据。
Hive默认有一个default数据库。默认的default目录是Hive的根目录。
一般来说,Hive不支持行级别的增删改。但是Hadoop 2.x后引入了追加操作。所以新的Hive支持插入操作:
但是我们更多的情况是把外部的数据通过Hive导入HDFS。这使用:
这会把这个文件放到HDFS中表的目录下。Hive所有针对表的查询实际上就在在表目录下对各种数据文件的查询。
下面看一个导入外部数据的简单的例子:
我们在外部创建一个文本文件,作为数据源使用:
[root@hadoop1 bin]# vim /home/data/stus.txt
1|zhang|male
2|li|female
3|pa|male
4|lili|male
5|mk|female
注意我们使用”|”作为每个数据的分隔符,需要告诉Hive我们使用的分隔符,在创建表的时候,我们加上format即可。
hive> create table student(id int, name string, gender string)row format delimited fields terminated by '|';
OK
Time taken: 0.508 seconds
随后导入数据,查询导入的结果:
hive> load data local inpath '/home/data/stus.txt' into table student;
Loading data to table test.student
Table test.student stats: [numFiles=1, totalSize=59]
OK
Time taken: 0.402 seconds
hive> select * from student;
OK
1 zhang male
2 li female
3 pa male
4 lili male
5 mk female
Time taken: 0.257 seconds, Fetched: 5 row(s)
除了我们要真实储存的数据,Hive还需要储存一些元数据。例如,我们创建了哪些数据库,数据库中有哪些表等。
Hive把元数据储存到一个关系型数据库中,那么这个数据库就是Hive的元数据库。在默认情况下,Hive使用内置的Derby数据库储存元数据。
Derby会在执行Hive的地方创建一个derby.db保存数据。所以如果你使用Derby,就表示你必须一直在一个目录下操作Hive。一旦切换目录,Hive的元数据会丢失。
下面是使用Derby丢失元数据的示例:
[root@hadoop1 bin]# ./hive
Logging initialized using configuration in jar:file:/usr/develop/hive1.2/lib/hive-common-1.2.0.jar!/hive-log4j.properties
hive> show databases;
OK
default
park
test
Time taken: 0.799 seconds, Fetched: 3 row(s)
hive> exit;
[root@hadoop1 bin]# cd ..
[root@hadoop1 hive1.2]# bin/hive
Logging initialized using configuration in jar:file:/usr/develop/hive1.2/lib/hive-common-1.2.0.jar!/hive-log4j.properties
hive> show databases;
OK
default
Time taken: 0.809 seconds, Fetched: 1 row(s)
我们看到一旦切换到Hive的根目录下执行Hive,之前创建的数据库就全部丢失了。
这样储存元数据的方式显得不太人道。除了Derby,Hive还支持使用MySQL作为元数据库。
首先,需要在Linux中安装MySQL。
在准备好MySQL之后,进入conf目录,使用Hive的模板配置文件更名为hive-site.xml:
[root@hadoop1 hive1.2]# cd conf/
[root@hadoop1 conf]# ls
beeline-log4j.properties.template hive-env.sh.template hive-log4j.properties.template
hive-default.xml.template hive-exec-log4j.properties.template ivysettings.xml
[root@hadoop1 conf]# cp hive-default.xml.template hive-site.xml
这个配置文件中有很多默认的配置项。我们关心的是如下配置:
<property>
<name>javax.jdo.option.ConnectionURLname>
<value>jdbc:mysql://hadoop1:3306/hive?createDatabaseIfNotExist=truevalue>
property>
<property>
<name>javax.jdo.option.ConnectionDriverNamename>
<value>com.mysql.jdbc.Drivervalue>
property>
<property>
<name>javax.jdo.option.ConnectionUserNamename>
<value>xxxvalue>
property>
<property>
<name>javax.jdo.option.ConnectionPasswordname>
<value>xxxvalue>
property>
其中,分别配置了数据库url,驱动程序,连接的用户名和密码。应该根据实际情况进行配置。
注意需要把MySQL的驱动文件放到hive的lib目录下。
注意一点,Hive的元数据库的编码集必须是latin1。如果不是,会出现奇怪的问题。
以上都没有问题的话,就能够正常使用Hive了。
Hive中默认建立的是内部表。这种表一旦被建立,就会在HDFS中存在有目录。把数据写入表的过程实际上就是把数据写入HDFS的过程。
内部表的特点是:先有表,后有数据。
但是还有一种情况:数据已经在HDFS中存好了,我们只是需要创建一张表去关联这些已经存在的数据。
外部表的特点是:现有数据,后有表。
下面做一个演示,先向HDFS中上传数据:
[root@hadoop1 bin]# more /home/data/persons
zhang|18
li|20
wang|28
zhao|30
qian|20
[root@hadoop1 bin]# hadoop fs -mkdir /person
[root@hadoop1 bin]# hadoop fs -put /home/data/persons /person/data
可见我们将数据上传到了/person/data中。
接下来,创建外部表,让这张表链接上这些数据,语法是:
和内部表的区别是多了external和location。location用来表示数据的位置。
hive> create external table person(name string, age int)row format delimited fields terminated by '|' location '/person';
OK
Time taken: 0.218 seconds
hive> select * from person;
OK
zhang 18
li 20
wang 28
zhao 30
qian 20
Time taken: 0.336 seconds, Fetched: 5 row(s)
在这种情况下,就不会到/user/hive/db下创建表文件夹了。
内部表和外部表的另外一个区别是,如果删除内部表,那么HDFS中对应的文件夹将会被删除。而删除外部表,HDFS中对应的文件夹则不会被删除。
Hive也支持分区表,也就是可以对数据进行分区操作,从而提高查询的效率。
分区表需要一个分区字段。对于字段的不同值,会储存在不同的目录下。
创建分区表的语法为:
其中,partitioned by指定分区的字段。
例如,我们现在有两个不同的文件,分别表示1月份和2月份的学生考试成绩:
[root@hadoop1 bin]# cat /home/data/score/1
zhang 70
li 90
wang 78
[root@hadoop1 bin]# cat /home/data/score/2
zhang 98
li 86
wang 79
我们希望在建表的时候按照月份进行分区,建表如下:
hive> create table score(name string) partitioned by(month int) row format delimited fields terminated by ' ';
OK
Time taken: 0.371 seconds
在加载数据的时候,需要附带这份数据的分区信息:
hive> load data local inpath '/home/data/score/1' into table score partition(month=1);
Loading data to table park.score partition (month=1)
Partition park.score{month=1} stats: [numFiles=1, numRows=0, totalSize=23, rawDataSize=0]
OK
Time taken: 0.861 seconds
hive> load data local inpath '/home/data/score/2' into table score partition(month=2);
Loading data to table park.score partition (month=2)
Partition park.score{month=2} stats: [numFiles=1, numRows=0, totalSize=23, rawDataSize=0]
OK
Time taken: 0.337 seconds
partition指明了上传数据的分区信息。
在使用的时候,操作和一般表差别不大,但是使用分区字段进行限制的话,查询效率会提升很多:
hive> select * from score where month=1;
OK
zhang 1
li 1
wang 1
Time taken: 0.386 seconds, Fetched: 3 row(s)
hive> select * from score where month=2;
OK
zhang 2
li 2
wang 2
Time taken: 0.083 seconds, Fetched: 3 row(s)
在使用分区表的时候,查询语句应该尽量都加上分区字段作为限定条件。
Hive底层实际上就是把分区表保存为多个文件夹。然后在元数据库中记录这些文件夹下的位置。
分区信息可以有多个,在实际情况中,发现如果经常需要按照某个字段进行查询并且数据量非常大,则可以把这些字段设置为分区字段。
分桶表对于数据的区分比分区表更加细粒度。分桶的原理就是根据某列数据进行Hash算法转换后储存在不同的区域。在查询的时候,如果对分桶列进行了限定,那么就会根据限定数据的Hash值到对应的区域中去查询数据。
分桶的一个重要作用是,在数据量非常大的时候,可以利用分桶筛选部分数据进行测试。
分桶和分区是可以放在一起使用的。
创建分桶表的语法是:
clustered by指明了按照哪个字段进行分桶。#表示分成几个桶。
注意在默认情况下,Hive的分桶是关闭的,需要执行以下语句打开Hive的分桶功能:
分组表无法使用load data。这样不会进行真正的分桶。
在实际中,我们一般创建一个测试表,在测试表中添加分桶功能。把真正的数据表的数据导入测试表,这样Hive就会进行分桶操作。
从一个表将数据导入另一个表的语法是:
这个导入的过程需要MapReduce的参与才可以把数据分开。
下面我们做一个演示,为之前的student表创建一个辅助的分桶表:
hive> create table student_temp(id int, name string) clustered by (id) into 2 buckets row format delimited fields terminated by '|';
OK
Time taken: 0.087 seconds
hive> set hive.enforce.bucketing=true;
hive> insert overwrite table student_temp select * from student;
...
我们将student_temp按照id分为了2桶。在HDFS底层实际上就是分文件夹进行存放。
这样在对id进行查询的时候会根据查询数据的Hash值到对应的目录下去查询。
如果要根据桶对数据进行取样,可以使用下面的语法:
其中,tablesample就表示取样,bucket后的数字表示取出第几个桶,out of后的数字表示一共有几个桶,field表示分桶字段。
下面取出第2个桶:
hive> select * from student_temp tablesample(bucket 2 out of 2 on id);
OK
5 mk
3 pa
1 zhang
Time taken: 0.352 seconds, Fetched: 3 row(s)
一共有5个数据,我们取出了其中的3个,这就实现了分桶。
Hive提供了很多内置函数(具体请参阅API文档),我们可以使用这些函数来加快开发效率。
当然,我们也可以自定义一个函数供我们使用。
我们首先需要把相关的包导入到我们的项目中。
我们开发一个将字符串转换为大写的功能,这需要我们新建一个继承自UDF的类,并且自己写一个evaluate方法:
package cn.lazycat.bdd.hive.udf;
import org.apache.hadoop.hive.ql.exec.UDF;
public class MyToUpper extends UDF {
public String evaluate(String str) {
return str.toUpperCase();
}
}
下面把项目打包成jar包,并在Hive中执行addJar命令将jar包传递给Hive。并创建自定义函数。
hive> add jar /root/MyToUpperCase.jar;
Added [/root/MyToUpperCase.jar] to class path
Added resources: [/root/MyToUpperCase.jar]
hive> create temporary function MyToUpper as 'cn.lazycat.bdd.hive.udf.MyToUpper';
随后就可以像使用正常函数一样使用自定义函数了。
Hive实现了JDBC标准,我们可以使用JDBC来间接操作Hive。
在使用前,我们需要启动Hive的服务:
[root@hadoop1 hive1.2]# bin/hive --service hiveserver2 &
加上”&”可以防止这个程序一直占据控制台。
随后开发客户端,需要导入hive-jdbc这个包,并且编写JDBC代码:
package cn.lazycat.bdd.hive.jdbc;
import java.sql.*;
public class HiveJDBCDemo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("org.apache.hive.jdbc.HiveDriver");
Connection conn = DriverManager.getConnection(
"jdbc:hive2://192.168.117.51:10000/park");
PreparedStatement statement = conn.prepareStatement("SELECT * FROM person");
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
System.out.print(resultSet.getString(1) + "\t");
System.out.println(resultSet.getString(2));
}
conn.close();
}
}
终端输出:
zhang 18
li 20
wang 28
zhao 30
qian 20