Hive最初由Facebook开发,最后贡献给Apache基金会,最后成为了Apache的顶级项目。Hadoop的出现虽然解决了海量数据的存储,并且提供了MR计算框架。但MR的使用门槛还是比较高,所以Facebook便开发了Hive,底层依旧使用HDFS存储数据,但操作数据却不用自己写MR程序,而是使用类似SQL的语法操作数据(在Hive里面称为HQL),后台自动转换为MapReduce,极大的降低了用户的使用难度。一句话概括就是:Hadoop提供了海量数据的存储、计算方案,而Hive使得该方案实施起来更加方便。
官方对于Hive的介绍如下:
The Apache Hive™ data warehouse software facilitates reading, writing, and managing large datasets residing in distributed storage and queried using SQL syntax.
对于Hive的总体介绍先到这里,我们先安装使用一下。
安装使用
Hive的安装也比较简单,和Hadoop那些类似,这里简单介绍一下步骤(本文使用的是Hive 2.3.3版本):
- 确保Hadoop已经正确安装,如果还没有,可参考我之前的文章《大数据系列一——Hadoop安装部署》。
下载二进制安装包并解压,然后设置
HIVE_HOME
和PATH
环境变量:export HIVE_HOME=$HOME/software/apache-hive-2.3.3-bin export PATH=$HIVE_HOME/bin:$PATH
在HDFS上面创建Hive需要的目录并设置权限:
$HADOOP_HOME/bin/hadoop fs -mkdir /tmp $HADOOP_HOME/bin/hadoop fs -mkdir /user/hive/warehouse $HADOOP_HOME/bin/hadoop fs -chmod g+w /tmp $HADOOP_HOME/bin/hadoop fs -chmod g+w /user/hive/warehouse
初始化Hive的Metastore:
$HIVE_HOME/bin/schematool -dbType derby -initSchema
为了后面能正常使用Hive自带的beeline,需要在$HADOOP_HOME/etc/hadoop/core-site.xml中增加如下设置(将里面的allan改为你自己的用户名):
<property> <name>hadoop.proxyuser.allan.groups</name> <value>*</value> </property> <property> <name>hadoop.proxyuser.allan.hosts</name> <value>*</value> </property>
同时,为了避免用户权限问题,在$HADOOP_HOME/etc/hadoop/hdfs-site.xml中关闭HDFS的权限校验:
<property> <name>dfs.permissions.enabled</name> <value>false</value> </property>
至此,一个最简单的Hive就安装完成了,我们可以通过Hive提供的CLI(Command Line Interface)访问Hive。Hive目前提供了两个CLI:HiveCLI(对应hive
命令)和Beeline(对应beeline
命令),前者现在已经废弃,不推荐使用;现在推荐使用Beeline。所以本文也就只介绍Beeline了。使用Beeline需要先启动HiveServer2((确保HDFS正常运行)):
$HIVE_HOME/bin/hiveserver2`。
默认HiveServer2监听端口为10000,我们使用Beeline连接Hive:
cd $HIVE_HOME
bin/beeline -u jdbc:hive2://localhost:10000
连接成功之后,我们可以执行一些HQL来感受一下Hive,后面部分再继续介绍涉及到的概念:
# 创建pokes表
0: jdbc:hive2://localhost:10000> CREATE TABLE pokes (foo INT, bar STRING);
No rows affected (0.412 seconds)
0: jdbc:hive2://localhost:10000> desc pokes;
+-----------+------------+----------+
| col_name | data_type | comment |
+-----------+------------+----------+
| foo | int | |
| bar | string | |
+-----------+------------+----------+
2 rows selected (0.195 seconds)
# 创建invites表,该表有一个分区
0: jdbc:hive2://localhost:10000> CREATE TABLE invites (foo INT, bar STRING) PARTITIONED BY (ds STRING);
No rows affected (0.158 seconds)
0: jdbc:hive2://localhost:10000> desc invites;
+--------------------------+-----------------------+-----------------------+
| col_name | data_type | comment |
+--------------------------+-----------------------+-----------------------+
| foo | int | |
| bar | string | |
| ds | string | |
| | NULL | NULL |
| # Partition Information | NULL | NULL |
| # col_name | data_type | comment |
| | NULL | NULL |
| ds | string | |
+--------------------------+-----------------------+-----------------------+
8 rows selected (0.234 seconds)
# 往pokes表里面导入数据
0: jdbc:hive2://localhost:10000> LOAD DATA LOCAL INPATH './examples/files/kv1.txt' OVERWRITE INTO TABLE pokes;
No rows affected (1.016 seconds)
# 查看数据
0: jdbc:hive2://localhost:10000> select * from pokes limit 5;
+------------+------------+
| pokes.foo | pokes.bar |
+------------+------------+
| 238 | val_238 |
| 86 | val_86 |
| 311 | val_311 |
| 27 | val_27 |
| 165 | val_165 |
+------------+------------+
5 rows selected (3.1 seconds)
# 往invites表里面导入数据,并指定分区
0: jdbc:hive2://localhost:10000> LOAD DATA LOCAL INPATH './examples/files/kv2.txt' OVERWRITE INTO TABLE invites PARTITION (ds='2008-08-15');
No rows affected (0.619 seconds)
0: jdbc:hive2://localhost:10000> LOAD DATA LOCAL INPATH './examples/files/kv3.txt' OVERWRITE INTO TABLE invites PARTITION (ds='2008-08-08');
No rows affected (0.619 seconds)
0: jdbc:hive2://localhost:10000> select * from invites limit 5;
+--------------+--------------+-------------+
| invites.foo | invites.bar | invites.ds |
+--------------+--------------+-------------+
| 238 | val_238 | 2008-08-08 |
| NULL | | 2008-08-08 |
| 311 | val_311 | 2008-08-08 |
| NULL | val_27 | 2008-08-08 |
| NULL | val_165 | 2008-08-08 |
+--------------+--------------+-------------+
5 rows selected (0.362 seconds)
# 创建新db
0: jdbc:hive2://localhost:10000> create database new_db;
No rows affected (0.547 seconds)
0: jdbc:hive2://localhost:10000> use new_db;
No rows affected (0.192 seconds)
0: jdbc:hive2://localhost:10000> CREATE TABLE pokes1 (foo INT, bar STRING);
No rows affected (0.216 seconds)
0: jdbc:hive2://localhost:10000> LOAD DATA LOCAL INPATH './examples/files/kv1.txt' OVERWRITE INTO TABLE pokes1;
No rows affected (0.524 seconds)
可以看到HQL和SQL非常的类似,本文的重点不是介绍HQL,所以这里就先不细述了,有兴趣的请移步LanguageManual.
数据模型
上一节我们看到Hive与传统的RDBMS非常的像,在Hive的数据模型里面主要包含如下一些概念:
- 数据库(Database):和RDBMS里面的数据库、HBase里面的Namespace作用类似,主要用来做资源隔离,避免一些名字冲突。同时也可以用来做权限管理。Hive里面内置了一个默认的数据库叫
default
。 - 表(Tables):和RDBMS里面的表类似,用于存放格式一样的数据。
- 分区(Partitions):每个表可以有多个分区列,用于决定数据如何存储,以及提高查询效率。分区列是一个虚拟列,并不是表中的某一列。实际使用中最常用的是使用时间列,比如上面的
invites
表中有一个分区列ds
,里面存储的是时间,这样当我们根据时间查询的时候,就可以只查询某些分区。需要注意的是:分区列名为2008-08-08并不代表里面所有的数据都是这一天的,这个要应用程序自己去保证。 - 桶(Buckets):有时也称作Clusters。对于每个分区里面的数据,我们可以根据哈希函数或者表中的某一列数据再分桶。
注意:分区和桶都是可选的,一个表可以没有分区和桶。
前面提到过,Hive的数据是存储在HDFS上的(存储目录由hive.metastore.warehouse.dir
指定,默认值为/user/hive/warehouse
),所以不同于RDBMS,Hive数据模型中的这些概念对应到后台实质就是一些目录(bucket可能是文件)。比如,我们看一下上一节中我们建的数据库、表以及数据是如何存储的:
➜ hadoop fs -ls -R /user/hive/warehouse
drwxrwxr-x - anonymous supergroup 0 2018-09-12 14:58 /user/hive/warehouse/invites
drwxrwxr-x - anonymous supergroup 0 2018-09-12 14:58 /user/hive/warehouse/invites/ds=2008-08-08
-rwxrwxr-x 1 anonymous supergroup 216 2018-09-12 14:58 /user/hive/warehouse/invites/ds=2008-08-08/kv3.txt
drwxrwxr-x - anonymous supergroup 0 2018-09-12 14:58 /user/hive/warehouse/invites/ds=2008-08-15
-rwxrwxr-x 1 anonymous supergroup 5791 2018-09-12 14:58 /user/hive/warehouse/invites/ds=2008-08-15/kv2.txt
drwxrwxr-x - anonymous supergroup 0 2018-09-12 15:13 /user/hive/warehouse/new_db.db
drwxrwxr-x - anonymous supergroup 0 2018-09-12 15:13 /user/hive/warehouse/new_db.db/pokes1
-rwxrwxr-x 1 anonymous supergroup 5812 2018-09-12 15:13 /user/hive/warehouse/new_db.db/pokes1/kv1.txt
drwxrwxr-x - anonymous supergroup 0 2018-09-12 14:57 /user/hive/warehouse/pokes
-rwxrwxr-x 1 anonymous supergroup 5812 2018-09-12 14:57 /user/hive/warehouse/pokes/kv1.txt
可以看到:
- 一个数据库对应一个目录,目录名格式为:数据库名.db,加后缀是为了和表名区分。因为内置的数据库
defalut
里面的表对应的目录和其它的数据库目录是平级的。而其它数据库里面的表则在数据库目录之下。 - 一个表名对应一个目录,目录名即表名;
- 一个分区对应一个目录,目录名即分区名,多个分区列的话就是多级目录。
- 数据以文件的方式存储在HDFS上面。
如果我们删掉数据库、表或者分区,对应的目录也会删除(外部表例外,以后再介绍)。
其他
Hive的设计目标
- easy data summarization
- easy ad-hoc querying
- easy analysis of large volumes of data
因为Hive底层使用Hadoop,同时做了数据抽象封装(库、表、分区、桶),还提供了类SQL的操作,使得上述的目标很容易达成。另外,Hive也支持用户自定义函数,增加了灵活性。
Hive不太适合的场景
- Hive不适合做实时性的分析计算。Hive是基于Hadoop的,之前介绍Hadoop的时候也提到过,Hadoop定位是离线计算。Hive的查询等到后台是MapReduce任务,而且是从HDFS读取数据,所以不适合做实时计算。Hive的定位是OLAP,而不是OLTP。
- Hive不适合做数据更新。Hive数据存储在HDFS上,而HDFS只支持append。老版本的Hive不支持Update和Delete操作,新版本的支持了,但最好还是避免使用,至少避免大量使用。如果有这种需求,应该考虑HBase等。
Hive的版本
如果看Hive的官方文档的话,一定会被各种版本号搞得吐血。文档里面使用的是一个版本号,而发布的又是另外一个版本号,我也没有逐一梳理过,官方给了几个:
PS:前面为Release的版本号,也就是我们经常说的Hive的版本号,而后面的是内部的版本号,主要在文档里面出现
- 1.0.0对应于0.14.1
- 1.1.0对应于0.15.0
- 2.3.0对应于2.2.0
关于Hive的版本策略参见:Understanding Hive Branches.
Hive为什么需要/tmp目录
首先Hive需要两个临时目录:
- HDFS上面的
/tmp/hive-<username>
(可以通过hive.exec.scratchdir
配置项修改) - Hive客户端所在机器的
/tmp/<username>
目录(硬编码,不可更改)
这些目录主要用于存储客户端查询时的一些临时数据或者中间数据,比如往Hive表中写数据,都是先写到HDFS上的临时目录,然后再move过去。正常情况下,客户端查询结束后会删掉这些临时数据,如果客户端异常,数据可能会残留。
Hive的配置文件
Hive最主要的配置文件为hive-site.xml,在$HIVE_CONF_DIR
指定的目录,默认为$HIVE_HOME/conf
。现在Hive的安装包里面没有hive-site.xml,只有hive-default.xml.template。后者是根据HiveConf.java文件里面的默认配置生成的,但Hive并不会使用这个配置文件。如果我们需要配置Hive,需要创建hive-site.xml文件,或者直接复制hive-default.xml.template为hive-site.xml,然后修改里面的值。
同时Hive还支持hivemetastore-site.xml和hiveserver2-site.xml两个配置文件,MetaStore启动的时候除了会加载hive-site.xml,还会加载hivemetastore-site.xml;HS2启动的时候除了会加载hive-site.xml,还会加载hiveserver2-site.xml,如果HS2使用的是Embedded的MetaStore,也会加载hivemetastore-site.xml。
整个配置的优先级从低到高依次为:
hive-site.xml -> hivemetastore-site.xml -> hiveserver2-site.xml -> '-hiveconf' commandline parameters -> set命令。
本文主要介绍了Hive的安装和基本使用,以及Hive的数据模型,可以说是非常粗的从“外面”介绍了一下Hive,旨在对Hive有一个直观的感受。下一篇文章会从“里面”介绍一下Hive,有兴趣的可以关注一下。
References:
详细实用
:roll: :roll: :roll: