当前位置: > > > Hive - 快速入门教程4(内部表、外部表、分区表、桶表、视图详解)

Hive - 快速入门教程4(内部表、外部表、分区表、桶表、视图详解)

    在 MySQL 中没有“表类型”这个概念,因为它里面的所有表都是一种类型的。但是 Hive 中有多种表类型:内部表、外部表、分区表和桶表。此外,Hive 中同样也拥有视图。本文将对各种表类型以及视图进行讲解。

一、内部表

1,基本介绍

(1)内部表也可以被称为受控表,它是 Hive 中的默认表类型,表数据默认存储在 HDFS 的“/user/hive/warehouse”目录下。

(2)当往内部表中加载数据时,即在使用 load 命令加载数据时,数据会被移动到“/user/hive/warehouse”目录下。

(3)当使用 drop 命令删除内部表时,表中的数据和元数据会被同时删除,这是内部表最典型的特性。

2,使用样例

(1)假设 /usr/local/t2.txt 文件中的内容如下:

(2)我们执行下面命令创建一张内部表 t2
create table t2(id int);

(3)然后执行如下命令可以将 /usr/local/t2.txt 文件加载到表 t2 中。
load data local inpath '/usr/local/t2.txt' into table t2;

(4)可以看到 HDFS 的“/user/hive/warehouse”目录下会有个 t2 目录,而数据则被移动到该目录下:

(5)如果我们执行如下命令删除 t2 表:
drop table t2;

(6)那么 HDFS 的“/user/hive/warehouse”目录下的 t2 目录及其内部数据也会被一并删除:

二、外部表

1,基本介绍

(1)建表语句中包含 External 关键字的表是外部表。

(2)当往外部表中加载数据时,数据并不会被移动到“/user/hive/warehouse”目录下,只是与外部数据建立一个链接(映射关系)。

(3)外部表的定义(元数据)和表中数据的生命周期互相不约束,表中数据只是表对 HDFS 上某一个目录的引用而已。在删除表定义时,表中数据依然是存在的,仅删除表和数据之间的引用关系。所以这种表是比较安全的,就算是误删表了,表中数据也不会丢。

(4)在创建外部表时,基本上都会通过 location 参数指定这个表对应的数据存储目录。当然也可以不指定,如果不指定,则使用默认的“/user/hive/warehouse”目录。
提示:在实际工作中,Hive 中 95% 以上的表都是外部表,因为大部分的数据都是由 Flume 这种日志采集工具提前采集到 HDFS 中的,此时使用 Hive 的外部表可以直接关联里面的数据,load 操作都可以省了。

2,使用样例

(1)假设 /usr/local/t2.txt 文件中的内容如下:

(2)我们执行下面命令创建一张外部表 t2
提示:这里在建表语句中通过 location 参数指定表数据存储的 HDFS 日录。
create external table t2(
  id int
) location '/data/t2';

(3)然后执行如下命令可以将 /usr/local/t2.txt 文件加载到表 t2 中。
load data local inpath '/usr/local/t2.txt' into table t2;

(4)查看表 t2 的数据:
select * from t2;

(5)而数据也在我们指定的存储目录下:

(6)接着我们删除表 t2
drop table t2;

(7)此时到 HDFS 上查看数据,发现“/data/t2”目录下的数据依然存在。这就是外部表的典型特性:在外部表被删除时,只会删除表的元数据,表中的数据不会被删除。

(8)我们再次创建 t2 表关联 HDFS 上的“/data/t2”目录:
create external table t2(
  id int
) location '/data/t2';

(9)数据也是可以成功查询到的:
select * from t2;

三、分区表

1,基本介绍

(1)分区表在实际工作中是非常常见的,应用场景特别多。
  • 假设企业中的 Web 服务器每天都产生一个日志文件,Flume 把日志数据采集到 HDFS 中,把每一天的数据都存储到 HDFS 的同一个目录下。
  • 如果要查询某一天的数据,则 Hive 默认会对所有文件都扫描一遍,然后过滤出需要查询的那一天的数据。
  • 如果这个采集任务已经运行了一年,那每次计算时都需要把一年的数据全部读取出来,再过滤出来某一天的数据,这样效率就太低了。
  • 我们可以让 Hive 在查询时,根据要查询的日期直接定位到对应的日期目录。这样即可直接查询满足条件的数据,效率提升至少上百倍。要实现这个功能,需要使用分区表。

(2)分区可以被理解为分类,通过分区把不同类型的数据存储到不同目录下。

(3)分区的标准就是指定的分区字段,分区字段可以有一个或多个。根据前面分析的场景,分区字段就是日期。

(4)分区表的意义在于优化查询。在查询时尽量利用分区字段,如果不使用分区字段,则会全表扫描。最典型的一个场景就是把“”作为分区字段,查询时指定天。

(5)分区表还可以被细分为内部分区表和外部分区表,主要区别就是在建表语句中是否使用了 External 关键字。
提示:在实际工作中,99% 的表都是外部分区表。

2,内部分区表使用样例

(1)首先我们创建一个内部分区表 t2
注意:建表语句中使用 partitioned by 指定了分区字段,分区字段的名称为 dt,类型为 string
create table t2(
  id int,
  name string
)
partitioned by(dt string)
row format delimited
fields terminated by '\t';

(2)查看内部分区表 t2 的信息:
desc t2;

(3)测试数据文件 t2.txt 内容如下:

(4)接着向内部分区表 t2 中加载数据,这里我使用不同的分区信息加载了两次。
注意:在向分区表中加载数据时需要指定分区信息,因为分区信息不需要在原始数据中存储。
load data local inpath '/usr/local/t2.txt' into table t2 partition(dt='2024-02-01');
load data local inpath '/usr/local/t2.txt' into table t2 partition(dt='2024-02-02');

(5)然后查看内部分区表 t2 中所有的分区信息:
show partitions t2;

(6)下面命令查看 t2 表的所有数据,会扫描全表:
select * from t2;

(7)下面命令只会查询 dt='2024-02-02'对应的 HDFS 目录下的数据,不会扫描全表,执行效率高。
select * from t2 where dt='2024-02-02';

(8)我们可以执行如下命令删除一个分区:
注意:此时分区删除之后,分区中对应的数据也就没有了,因为是内部表,所以分区的数据是会被删掉的。
alter table t2 drop partition(dt='2024-02-02');

(9)针对一张表我们也可以同时创建多个分区,比如下面的学生表,我们使用年份和学院名称作为分区字段:
create table student(
  id int,
  name string
) partitioned by (year int, school string)
row format delimited
fields terminated by '\t';
  • 然后在加载时指定 yearschool 这两个分区字段即可:
load data local inpath '/usr/local/student.data' into table student partition (year=2020,school='xk');

3,外部分区表使用样例

(1)首先我们创建一个外部分区表 t1
注意:建表语句中使用 partitioned by 指定了分区字段,分区字段的名称为 dt,类型为 string。通过 location 参数指定表数据存储的 HDFS 日录。
create external table t1(
  id int,
  name string
)
partitioned by(dt string)
row format delimited
fields terminated by '\t'
location '/data/t1';

(2)查看内部分区表 t1 的信息:
desc t1;

(3)测试数据文件 t1.txt 内容如下:

(4)接着向外部分区表 t1 中加载数据,这里我使用不同的分区信息加载了两次。
注意:在向分区表中加载数据时需要指定分区信息,因为分区信息不需要在原始数据中存储。
load data local inpath '/usr/local/t1.txt' into table t1 partition(dt='2024-02-01');
load data local inpath '/usr/local/t1.txt' into table t1 partition(dt='2024-02-02');

(5)然后查看内部分区表 t1 中所有的分区信息:
show partitions t1;

(6)下面命令查看 t1 表的所有数据,会扫描全表:
select * from t1;

(7)下面命令只会查询 dt='2024-02-02' 对应的 HDFS 目录下的数据,不会扫描全表,执行效率高。
select * from t1 where dt='2024-02-02';

(8)在实际工作中会遇到这种场景:数据已经被提前通过 Flume 或者其他组件采集到 HDFS 的指定目录下了,如果不关联 Hive 是查询不到数据的。比如 HDFS 的“/data/t2”目录下有一些已有的数据文件(可以是多个文件):

(9)我们需要将 HDFS 中的数据和 Hive 中的外部分区表进行关联。下面命令表示将 HDFS 中“/data/t2”目录下的数据关联到 Hive 中表 t1 dt='2024-03-01' 这个分区中。
提示:
  • load data …partition …:这条命令做了两件事情:移动数据;添加分区。
  • alter table …add partition ….location …:这条命令做了一件事情:添加分区。
alter table t1 add partition(dt='2024-03-01') location '/data/t2';

(10)接下来查询一下外部分区表 t1 数据,可以看到数据被添加进来了。
select * from t1;

四、桶表

1,基本介绍

(1)桶表是对数据进行哈希取值,然后放到不同文件中存储。在物理层面,每个桶就是表(或分区)中的一个文件。

(2)桶表的主要作用有两点:
  • 在数据抽样时提高效率。
  • 提高 JOIN 操作的查询效率。

(3)例如:针对中国的人口,有些省份的人口相对较少。如果使用分区表,把省份作为分区字段,则数据会集中在某几个分区中,其他分区中的数据就很少。这样对数据存储及查询不太友好,在计算时会出现数据倾斜的问题,计算效率也不高。应该相对均匀地存放数据,从源头上解决问题,这时可以使用桶表。
提示:在实际工作中,桶表的应用场景比较少,不是日常用来存储数据的表,需要抽样查询时,才创建和使用桶表。

2,使用样例

(1)创建普通表:
create table course_common(
  id int,
  name string,
  score int
)
row format delimited
fields terminated by "\t"; 

(2)创建分桶表:
create table course(
  id int,
  name string,
  score int
)
clustered by (id) into 3 buckets
row format delimited
fields terminated by "\t";

(3)普通表加载数据:
load data local inpath '/usr/local/course.txt' into table course_common;
  • 其中 course.txt 文件中的数据如下:
 
(4)通过 insert...select...给桶表加载数据:
insert into table course select * from course_common;

(5)查看分桶表 course 里面数据:
select * from course;

(6)同时 HDFS 上对应 couse 表目录下确实也根据桶的数量将数据分成 3 个文件:

(7)桶表的一个主要作用就是数据抽样。假如我们使用的是一个大规模的数据集,我们只想去抽取部分数据进行查看.使用 bucket 表可以变得更加的高效。
  • 比如下面代码根据 id 对桶表中的数据重新分桶,分成 3 桶,取出第 1 桶的数据。
select * from course tablesample(bucket 1 out of 3 on id);

(8)桶表的另一个主要作用就是提高某些查询效率。例如:join 查询,可以避免产生笛卡尔积的操作。
  • 比如下面样例,如果 a 表和 b 表已经是分桶表,而且分桶的字段是 id 字段,那么做这个操作的时候就不需要再进行全表笛卡尔积了,因为分桶之后相同规则的 id 已经在相同的文件里面了,这样 a 表的每个桶就可以和 b 表的每个桶直接 join,而不用全表 join 了。
select a.id,a.name,b.addr from a join b on a.id = b.id;

五、视图

1,基本介绍

Hive 中也有视图的功能,其主要作用是降低查询的复杂度。可以通过 create view 语句创建视图。

2,使用样例

(1)首先执行如下命令创建一个视图:
create view v1 as select course.id, course.name from course;

(2)执行如下命令查看视图结构:
desc v1;

(3)通过视图查看数据。
select * from v1;
评论0