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版本):

  1. 确保Hadoop已经正确安装,如果还没有,可参考我之前的文章《大数据系列一——Hadoop安装部署》。
  2. 下载二进制安装包并解压,然后设置HIVE_HOMEPATH环境变量:

    export HIVE_HOME=$HOME/software/apache-hive-2.3.3-bin
    export PATH=$HIVE_HOME/bin:$PATH
  3. 在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
  4. 初始化Hive的Metastore:

    $HIVE_HOME/bin/schematool -dbType derby -initSchema
  5. 为了后面能正常使用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.templatehive-site.xml,然后修改里面的值。

同时Hive还支持hivemetastore-site.xmlhiveserver2-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: