Hive之数据模型

(本文是基于多篇文章根据个人理解进行的整合,参考的文章见末尾的整理)

数据模型


hive的数据模型包括:databasetablepartitionbucket

1.Database
相当于关系数据库里的命名空间(namespace),它的作用是将用户和数据库的应用隔离到不同的数据库或模式中,该模型在hive 0.6.0之后的版本支持,hive提供了create database dbnameuse dbname以及drop database dbname这样的语句。

2.
表(table):hive的表逻辑上由存储的数据和描述表格中的数据形式的相关元数据组成。元数据存储在关系数据库里。表存储的数据存放在hive的数据仓库中,这个数据仓库也就是hdfs上的一个目录,该目录是在hive-site.xml中由${hive.metastore.warehouse.dir}指定的,这里假定为/user/hive/warehouse/。创建一张hive的表,就是在hdfs的仓库目录下创建一个文件夹。表分为内部表和外部表两种。

2.1
元数据
Hive将元数据存储在RDBMS中,一般常用的有MYSQLDERBY
Apache Derby
非常小巧,核心部分derby.jar只有2M,所以既可以做为单独的数据库服务器使用,也可以内嵌在应用程序中使用。所以hive采用了Derby作为一个内嵌的元数据库,可以完成hive安装的简单测试。 hive安装完成之后,就可以在hive shell中执行一些基本的操作,创建表、查询等等。如果你细心的话,就会发现一个问题:当在某个目录下启动终端,进入hive shell时,hive默认会在当前目录下生成一个derby文件和一个metastore_db目录,这两个文件主要保存刚刚在shell中操作的一些sql的结果,比如新建的表、添加的分区等等。
这种方式的弊端 :1.
在同一个目录下同时只能有一个hive客户端能使用数据库;2.切换目录启动新的shell,无法查看之前创建的表,不能实现表数据的共享。由于这些弊端,所以采用mysql保存hive元数据解决上面的问题。hive所有的元数据都保存在同一个库里,这样不同开发者创建的表可以实现共享。
hive元数据对应的表约有20个,其中和表结构信息有关的有9张,其余的10多张或为空,或只有简单的几条记录,以下是部分主要表的简要说明。
表名
说明
关联键
TBLS
所有hive表的基本信息
TBL_ID,SD_ID
TABLE_PARAM
表级属性,如是否外部表,表注释等
TBL_ID
COLUMNS
Hive表字段信息(字段注释,字段名,字段类型,字段序号)
SD_ID
SDS
所有hive表、表分区所对应的hdfs数据目录和数据格式
SD_ID,SERDE_ID
SERDE_PARAM
序列化反序列化信息,如行分隔符、列分隔符、NULL的表示字符等
SERDE_ID
PARTITIONS
Hive表分区信息
PART_ID,SD_ID,TBL_ID
PARTITION_KEYS
Hive分区表分区键
TBL_ID
PARTITION_KEY_VALS
Hive表分区名(键值)
PART_ID
   从上面表的内容来看,hive整个创建表的过程已经比较清楚了。
   1)解析用户提交hive语句,对其进行解析,分解为表、字段、分区等hive对象
   2)根据解析到的信息构建对应的表、字段、分区等对象,从SEQUENCE_TABLE中获取构建对象的最新ID,与构建对象信息(名称,类型等)一同通过DAO方法写入到元数据表中去,成功后将SEQUENCE_TABLE中对应的最新ID+5
实际上我们常见的RDBMS都是通过这种方法进行组织的,典型的如postgresql,其系统表中和hive元数据一样裸露了这些id信息(oid,cid等),而Oracle等商业化的系统则隐藏了这些具体的ID。通过这些元数据我们可以很容易的读到数据诸如创建一个表的数据字典信息,比如导出建表语名等。

2.2
内部表
内部表数据文件存储在hive的数据仓库里。删除表时,元数据与数据都会被删除。
简单实例:
1 在本地创建数据文件:/home/test_inner_data.txt
2 创建表:Create table test_inner_table (key string)
  注:/user/hive/warehouse/录下创建一个名为 test_inner_table的文件夹
3 加载数据:Load local inpath '/home/test_inner_data.txt'  into table test_inner_table
  注:将/home/test_inner_data.txt→ 复制到hdfs:/home/hdfs/test_inner_data.txtHive中进行配置,临时保存)→移动到hdfs:/user/hive/warehouse/test_inner_table/test_inner_data.txt。hive加载数据时候不会对元数据进行任何检查,只是简单的移动文件的位置,如果源文件格式不正确,也只有在做查询操作时候才能发现,那个时候错误格式的字段会以NULL来显示。
4 查看数据:Select * from test_inner_table;
5 删除表:Drop table test_inner_table
  注:test_inner_table文件夹以及包含的所有数据被移到hdfs:/user/hdfs/.Trash/Current文件夹中(如果你的Hadoop没有取用垃圾箱机制,那么drop table 命令将会把其删除!)

2.3
外部表
外部表的数据文件可以存放在hive数据仓库外部的分布式文件系统上,也可以放到hive数据仓库里。外部表实际数据是存储在LOCATION后面指定的HDFS路径中,若不指定则移动到数据仓库目录中。当删除一个ExternalTable时,仅删除该元数据。
简单示例:
1 在本地创建数据文件:/home/test_external_data.txt
2 创建表:Create external table test_external_table (key string)  Location '/home/hadoop/external';
  注:不创建任何文件夹。若不指定location,则默认在hdfs://user/hive/warehouse/下创建一个test_external_table文件夹;
3 加载数据:Load data local inpath '/home/test_external_data.txt' into table test_external_table;
  注:将/home/test_external_data.txt→ 复制到hdfs:/home/hdfs/test_external_data.txt→移动到hdfs:/home/hadoop/external/test_external_data.txt。若不指定location,将/home/test_external_data.txt → 复制到hdfs:/home/hdfs/test_external_data.txt → 移动到hdfs:/user/hive/warehouse/test_external_table/test_external_data.txt
4 查看数据:select * from test_external_table;
5 删除表:drop table test_external_table
  注:hdfs:/home/hadoop/external/test_external_data.txt并没有被删除。若不指定location,待定

3.分区(partition
hive里分区的概念是根据“分区列”的值对表的数据进行粗略划分的机制, 在hive存储上就体现在表的主目录下的一个子目录,这个文件夹的名字就是我们定义的“分区列+值”。
分区以字段的形式存在表结构中,通过describe table命令可以查看到字段存在,但并不是对应着数据文件中某个列的字段,它不存放实际的数据内容,仅仅是分区的表示(伪列)。
至于用户存储的每个数据文档到底放到哪个分区,由用户决定,只是单纯的数据文档的移动。即用户在加载数据的时候必须显示的指定该部分数据放到哪个分区。

进行分区的好处:(1)提高查询效率。
在Hive Select查询中一般会扫描整个表内容,会消耗很多时间做没必要的工作。有时候只需要扫描表中关心的一部分数据,因此建表时引入了partition概念。
如:当前互联网应用每天都要存储大量的日志文件,几G、几十G甚至更大都是有可能。存储日志,其中必然有个属性是日志产生的日期。在产生分区时,就可以按照日志产生的日期列进行划分。把每一天的日志当作一个分区。 

   具体例子如下:

1. 创建一个分区表,以 ds 为分区列: 
create table invites (id int, name string)
partitioned by (ds string)
row format delimited
fields terminated by 't' stored as textfile; 

2. 将数据添加到时间为 2013-08-16 这个分区中: 
load data local inpath '/home/hadoop/Desktop/data.txt' overwrite into table invites partition (ds='2013-08-16'); 
3. 从一个分区中查询数据: 
select * from invites where ds ='2013-08-16'; 
4. 往一个分区表的某一个分区中添加数据: 
insert overwrite table invites
partition (ds='2013-08-16')
select id,max(name) from test group by id; 

5. 可以查看分区的具体情况,使用命令: 
hadoop fs -ls /home/hadoop.hive/warehouse/invites 或者 show partitions tablename;

 4. Hive 桶

对于每一个表(table)或者分区, Hive可以进一步组织成桶,也就是说桶是更为细粒度的数据范围划分。

是对数据源数据文件本身来拆分数据,使用桶的表会将源数据文件按一定规律拆分成多个文件。物理上,每个桶就是表(或分区)目录里的一个文件

Hive也是针对某一列进行桶的组织,这里的列字段是对应于数据文件中具体某个列的。

Hive采用对列值哈希,然后除以桶的个数求余的方式决定该条记录存放在哪个桶当中。

 

   把表(或者分区)组织成桶(Bucket)好处:

(1)获得更高的查询处理效率。桶为表加上了额外的结构,Hive 在处理有些查询时能利用这个结构。具体而言,连接两个在(包含连接列的)相同列上划分了桶的表,可以使用 Map 端连接 (Map-side join)高效的实现。比如JOIN操作。对于JOIN操作两个表有一个相同的列,如果对这两个表都进行了桶操作。那么将保存相同列值的桶进行JOIN操作就可以,可以大大较少JOIN的数据量。

(2)使取样(sampling)更高效。在处理大规模数据集时,在开发和修改查询的阶段,如果能在数据集的一小部分数据上试运行查询,会带来很多方便。

 

  具体例子如下:

  1. 创建带桶的 table :

create table bucketed_user(id int,name string)
clustered by (id) sorted by(name) into 4 buckets
row format delimited fields terminated by '\t' stored as textfile; 
使用CLUSTERED BY 子句来指定划分桶所用的列和要划分的桶的个数。

对于map端连接的情况,两个表以相同方式划分桶。处理左边表内某个桶的 mapper知道右边表内相匹配的行在对应的桶内。因此,mapper只需要获取那个桶 (这只是右边表内存储数据的一小部分)即可进行连接。这一优化方法并不一定要求 两个表必须桶的个数相同,两个表的桶个数是倍数关系也可以。用HiveQL对两个划分了桶的表进行连接,可参见“map连接”部分(P400)。
桶中的数据可以根据一个或多个列另外进行排序。由于这样对每个桶的连接变成了高效的归并排序(merge-sort), 因此可以进一步提升map端连接的效率。以下语法声明一个表使其使用排序桶:
CREATE TABLE bucketed_users (id INT, name STRING) 
CLUSTERED BY (id) SORTED BY (id ASC) INTO 4 BUCKETS; 

我们如何保证表中的数据都划分成桶了呢?把在Hive外生成的数据加载到划分成 桶的表中,当然是可以的。其实让Hive来划分桶更容易。这一操作通常针对已有的表。
Hive并不检查数据文件中的桶是否和表定义中的桶一致(无论是对于桶 的数量或用于划分桶的列)。如果两者不匹配,在査询时可能会碰到错 误或未定义的结果。因此,建议让Hive来进行划分桶的操作。
有一个没有划分桶的用户表: 
hive> SELECT * FROM users; 
0    Nat 
2    Doe 
B    Kay 
4    Ann 

2. 强制多个 reduce 进行输出:

要向分桶表中填充成员,需要将 hive.enforce.bucketing 属性设置为 true。①这 样,Hive 就知道用表定义中声明的数量来创建桶。然后使用 INSERT 命令即可。
需要注意的是: clustered by和sorted by不会影响数据的导入,这意味着,用户必须自己负责数据如何如何导入,包括数据的分桶和排序。 
'set hive.enforce.bucketing = true' 可以自动控制上一轮reduce的数量从而适配bucket的个数,当然,用户也可以自主设置mapred.reduce.tasks去适配bucket个数,推荐使用'set hive.enforce.bucketing = true'  

3. 往表中插入数据:

INSERT OVERWRITE TABLE bucketed_users SELECT * FROM users; 
物理上,每个桶就是表(或分区)目录里的一个文件。它的文件名并不重要,但是桶 n 是按照字典序排列的第 n 个文件。事实上,桶对应于 MapReduce 的输出文件分区:一个作业产生的桶(输出文件)和reduce任务个数相同。我们可以通过查看刚才 创建的bucketd_users表的布局来了解这一情况。运行如下命令:  

4. 查看表的结构:

hive> dfs -ls /user/hive/warehouse/bucketed_users; 
将显示有4个新建的文件。文件名如下(文件名包含时间戳,由Hive产生,因此 每次运行都会改变): 
attempt_201005221636_0016_r_000000_0 
attempt_201005221636_0016_r-000001_0 
attempt_201005221636_0016_r_000002_0 
attempt_201005221636_0016_r_000003_0 
第一个桶里包括用户ID 0和4,因为一个INT的哈希值就是这个整数本身,在这里 除以桶数(4)以后的余数:② 

5. 读取数据,看每一个文件的数据:

hive> dfs -cat /user/hive/warehouse/bucketed_users/*0_0; 
0 Nat 
4 Ann 
用TABLESAMPLE子句对表进行取样,我们可以获得相同的结果。这个子句会将查询限定在表的一部分桶内,而不是使用整个表:

6. 对桶中的数据进行采样:

hive> SELECT * FROM bucketed_users 
>    TABLESAMPLE(BUCKET 1 OUT OF 4 ON id); 
0 Nat 
4 Ann 
桶的个数从1开始计数。因此,前面的查询从4个桶的第一个中获取所有的用户。 对于一个大规模的、均匀分布的数据集,这会返回表中约四分之一的数据行。我们 也可以用其他比例对若干个桶进行取样(因为取样并不是一个精确的操作,因此这个 比例不一定要是桶数的整数倍)。
例如,下面的查询返回一半的桶:

7. 查询一半返回的桶数:

hive> SELECT * FROM bucketed_users 
>    TABLESAMPLE(BUCKET 1 OUT OF 2 ON id); 
0 Nat 
4 Ann 
2 Joe 
因为查询只需要读取和TABLESAMPLE子句匹配的桶,所以取样分桶表是非常高效 的操作。如果使用rand()函数对没有划分成桶的表进行取样,即使只需要读取很 小一部分样本,也要扫描整个输入数据集: 

hive〉 SELECT * FROM users 
> TABLESAMPLE(BUCKET 1 OUT OF 4 ON rand()); 
2 Doe
①从Hive 0.6.0开始,对以前的版本,必须把mapred.reduce .tasks设为表中要填 充的桶的个数。如果桶是排序的,还需要把hive.enforce.sorting设为true。 
②显式原始文件时,因为分隔字符是一个不能打印的控制字符,因此字段都挤在一起。 

参考:

Hive介绍 http://www.cnblogs.com/sharpxiajun/archive/2013/06/02/3114180.html
Hive的数据类型和数据模型 http://www.cnblogs.com/sharpxiajun/archive/2013/06/03/3114560.html
Hive内表与外表详述 http://www.aboutyun.com/thread-7458-1-1.html
Hive基础之分区和桶 http://my.oschina.net/leejun2005/blog/178631
Hive的体系结构 http://blog.csdn.net/zhoudaxia/article/details/8855937
Hive的元数据库配置,metadata相关 http://www.2cto.com/database/201411/352706.html
Hadoop Hive SQL语法详解 http://blog.csdn.net/hguisu/article/details/7256833
Hive几种数据导入方式 http://www.iteblog.com/archives/94
Hive专栏 http://www.iteblog.com/archives/category/hive
Hive专栏 http://my.oschina.net/leejun2005/blog?catalog=384549
Hive资料集锦 http://my.oschina.net/leejun2005/blog/140462#OSC_h3_2
Programing Hive http://flyingdutchman.iteye.com/category/277341
Hive官网 https://cwiki.apache.org/confluence/display/Hive/GettingStarted
 
posted @ 2014-12-24 18:00  syx_1987  Views(2718)  Comments(0Edit  收藏  举报