Hive关键概念

610 阅读6分钟

1、Hive数据库

Hive中的数据库从本质上来说仅仅是一个目录或者命名空间,但是对于具有很多用户和组的集群来说,这个概念非常有用。首先,这样可以避免表名冲突;其次,它等同于关系型数据库中的数据库概念,是一组表或者表的逻辑组,非常容易理解。

2、Hive表

Hive中的表(Table)和关系型数据库中的table在概念上类似的,每个table在Hive中都有一个相对应的目录存储数据,如果没有指定表的数据库,那么Hive会通过{Hive_HOME}/conf/hive-site.xml配置文件中的hive.metastore.warehouse.dir属性来使用默认值(一般是/user/hive/warehouse,也可以根据实际的情况来修改这个配置),所有的table数据(不包括外部表)都保存在这个目录中。

Hive表分为两类:即内部表和外部表。所谓内部表(managed table)即Hive管理的表,Hive内部表的管理既包含逻辑及语法上的,也包含实际物理意义上的,即创建Hive内部表时,数据将真实存于表所在的目录内,删除内部表时,物理数据和文件一并删除。外部表(external table)则不然,其管理仅仅是逻辑及语法上的,即新建表仅仅是指向一个外部目录而已。同样,删除时也并不删除外部目录,而仅仅是将引用和定义删除。
考虑下面的语句:

create table stu (name string);
load data inpath '/user/stu/stu.txt' into table stu

上述语句会将hdfs:///user/stu/stu.txt 移动到Hive的对应目录hdfs://user/hive/warehouse/stu。但是载入数据的速度非常快,因为Hive只是把数据移动到对应的目录,不会对数据是否符合定义的Schema做校检,这个工作通常在读取的时候进行(即为Schema On Read)
同时stu使用drop语句删除后,其数据和表的元数据信息都会被删除,不再存在,这就是Hive Managed的意思

drop table stu

外部表则不一样,数据的创建和删除完全由自己控制,Hive不管理这些数据。数据的位置在创建时指定:

create external table external_table (name string)
	location '/user/external/external_table'
load data inpath '/user/external/external_table.txt' into table external_table

指定external 关键字后,Hive不会把数据移动到warehouse目录中。事实上,Hive甚至不会校验外部表的目录是否存在。这使得我们可以在创建表之后再创建数据。当删除外部表时,Hive只删除元数据,而不会删除HDFS存储的数据

选择内部表还是选择外部表?
大多数情况下,这两者的区别不是很明显。如果数据的所有处理都在Hive中进行,那么更倾向于选择内部。但是如果Hive和其他工具针对相同的数据集做处理,那么外部表更适合。一种常见的模式是使用外部表访问存储的HDFS(通常由其它工具创建)中的初始数据,然后使用Hive转换数据并将其结果放在内部表中。相反,外部表也可以用于将Hive的处理结果导出供其他应用使用。使用外部表的另一种场景是针对一个数据集,关联多个Schema。

3、分区合桶

Hive将表划分为分区(partition),partition根据分区字段进行。分区可以让数据的部分查询变得更快。表或者分区可以进一步被划分为桶(bucket)。桶通常在原始数据中加入一些额外的结构,这些结构可以用于高效查询。例如,基于用户ID的分桶可以使基于用户的查询非常快

3.1、分区

假设日志分区中,每条记录都带有时间戳。如果根据时间来分区,那么同一天的数据将会被划分到同一个分区中。针对某一天或者几天的数据查询将会变得很高效,因为只需要读取对应分区的文件。分区并不会导致跨度大的查询变得低效。
分区可以通过多个维度来进行。例如通过分区划分之后,还可以根据国家进一步划分。分区在在创建表的时候使用Partitioned By从句定义,该从句接收一个字段列表:

create table partition_table (id int,name string) partitioned by (data string , country string);

当导入数据到分区表时,分区的值被显示指定:

load data inpath '/user/root/data' into table partition_table partition (data = '2020-11-01',country = 'cn');

在文件系统中,分区作为表的目录的下一级目录存在,如图所示: 实际SQL中,灵活指定分区将大大提高其效率,如下代码将仅会扫描2020-11-01目录下的目录:

select id , name from partition_table where data = '2020-11-01' and country = 'cn';

3.2、分桶

在表或者分区中使用桶通常有两个原因:一是为了高效查询。桶在表中加入了特殊的结果,Hive在查询的时候可以利用这些结构提高效率。例如,如果两个表根据相同的字段进行分桶,则在对这两个表进行关联的时候,可以使用map-side关联高效实现,前提是关联的字段在分桶字段中出现。二是可以高效地进行抽样。在分析大数据集时,经常需要对部分抽样数据进行观察和分析,分桶有利于高效实现抽样。
为了让Hive对表进行分桶,通过CLUSTERED BY从句在创建表的时候指定:

create table bucketed_table (id int ,name string)
clustered by (id) into 4 buckets;

指定表根据id字段进行分桶,并且分为4个桶。分桶时,Hive根据字段哈希后取余数来决定数据放在哪个桶,因此每个桶都是整体数据的随机抽样。
在map-side的关联中,两个表根据相同的字段进行分桶,因此处理左表的bucket时,可以直接从外表对应的bucket中提取数据进行关联操作。map-side关联的两个表不一定需要完全相同bucket数量,只要成倍数即可。
需要注意的是,Hive并不会对数据是否满足表定义中的分桶进行校验,只有在查询时出现异常才会报错。因此,一种更好的方式是将分桶的工作交给Hive来完成(设置hive.enfoce.bucketing属性为true即可)