Hive常用操作

本文遵循BY-SA版权协议,转载请附上原文出处链接。


本文作者: 黑伴白

本文链接: http://heibanbai.com.cn/posts/e2dcb65e/

数据库操作

创建数据库

1
2
3
4
5
6
-- 语法
CREATE [REMOTE] (DATABASE|SCHEMA) [IF NOT EXISTS] database_name
[COMMENT database_comment] -- 数据库注释
[LOCATION hdfs_path] -- 存储在 HDFS 上的位置,不指定location,则使用${hive.metastore.warehouse.dir}
[MANAGEDLOCATION hdfs_path] -- 指定管理表的存储路径(hive 4.0.0 才支持)
[WITH DBPROPERTIES (property_name=property_value, ...)]; -- 指定额外属性

示例:

1
2
3
4
5
hive (default)> create database if not exists hive_test
> comment 'hive database for test'
> with dbproperties ('cretae'='heibanbai');
OK
Time taken: 0.485 seconds

查看数据库列表

1
2
3
4
5
6
hive (default)> show databases;
OK
database_name
default
hive_test
Time taken: 0.16 seconds, Fetched: 2 row(s)

使用数据库

1
2
3
hive (default)> use hive_test;
OK
Time taken: 0.026 seconds

修改数据库

用户可以使用ALTER DATABASE命令为某个数据库的DBPROPERTIES设置键-值对属性值,来描述这个数据库的属性信息。

数据库的其他元数据信息都是不可更改的,包括数据库名和数据库所在的目录位置。

1
2
3
4
5
6
hive (default)> alter database hive_test set dbproperties('createtime'='20220517');
OK
Time taken: 8.537 seconds
hive (default)> alter database hive_test set dbproperties('edited-by'='Eric');
OK
Time taken: 0.058 seconds

查看数据库信息

1
2
3
4
5
6
7
8
9
10
11
hive (default)> desc database hive_test;
OK
db_name comment location owner_name owner_type parameters
hive_test hive database for test hdfs://node1:9000/user/hive/warehouse/hive_test.db hadoop USER
Time taken: 0.342 seconds, Fetched: 1 row(s)
# extended显示额外属性信息
hive (default)> desc database extended hive_test;
OK
db_name comment location owner_name owner_type parameters
hive_test hive database for test hdfs://node1:9000/user/hive/warehouse/hive_test.db hadoop USER {edited-by=Eric, createtime=20220517, cretae=heibanbai}
Time taken: 0.035 seconds, Fetched: 1 row(s)

删除数据库

1
2
3
4
5
# 默认行为是 RESTRICT,如果数据库中存在表则删除失败。要想删除库及其中的表,可以使用 CASCADE 级联删除
DROP (DATABASE|SCHEMA) [IF EXISTS] database_name [RESTRICT|CASCADE];

# 示例
DROP DATABASE IF EXISTS hive_test CASCADE;

数据库表操作

创建表语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name    -- (Note: TEMPORARY available in Hive 0.14.0 and later)
[(col_name data_type [column_constraint_specification] [COMMENT col_comment], ... [constraint_specification])]
[COMMENT table_comment]
[PARTITIONED BY (col_name data_type [COMMENT col_comment], ...)]
[CLUSTERED BY (col_name, col_name, ...) [SORTED BY (col_name [ASC|DESC], ...)] INTO num_buckets BUCKETS]
[SKEWED BY (col_name, col_name, ...) -- (Note: Available in Hive 0.10.0 and later)]
ON ((col_value, col_value, ...), (col_value, col_value, ...), ...)
[STORED AS DIRECTORIES]
[
[ROW FORMAT row_format]
[STORED AS file_format]
| STORED BY 'storage.handler.class.name' [WITH SERDEPROPERTIES (...)] -- (Note: Available in Hive 0.6.0 and later)
]
[LOCATION hdfs_path]
[TBLPROPERTIES (property_name=property_value, ...)] -- (Note: Available in Hive 0.6.0 and later)
[AS select_statement]; -- (Note: Available in Hive 0.5.0 and later; not supported for external tables)

CREATE [TEMPORARY] [EXTERNAL] TABLE [IF NOT EXISTS] [db_name.]table_name
LIKE existing_table_or_view_name
[LOCATION hdfs_path];

data_type
: primitive_type
| array_type
| map_type
| struct_type
| union_type -- (Note: Available in Hive 0.7.0 and later)

primitive_type
: TINYINT
| SMALLINT
| INT
| BIGINT
| BOOLEAN
| FLOAT
| DOUBLE
| DOUBLE PRECISION -- (Note: Available in Hive 2.2.0 and later)
| STRING
| BINARY -- (Note: Available in Hive 0.8.0 and later)
| TIMESTAMP -- (Note: Available in Hive 0.8.0 and later)
| DECIMAL -- (Note: Available in Hive 0.11.0 and later)
| DECIMAL(precision, scale) -- (Note: Available in Hive 0.13.0 and later)
| DATE -- (Note: Available in Hive 0.12.0 and later)
| VARCHAR -- (Note: Available in Hive 0.12.0 and later)
| CHAR -- (Note: Available in Hive 0.13.0 and later)

array_type
: ARRAY < data_type >

map_type
: MAP < primitive_type, data_type >

struct_type
: STRUCT < col_name : data_type [COMMENT col_comment], ...>

union_type
: UNIONTYPE < data_type, data_type, ... > -- (Note: Available in Hive 0.7.0 and later)

row_format
: DELIMITED [FIELDS TERMINATED BY char [ESCAPED BY char]] [COLLECTION ITEMS TERMINATED BY char]
[MAP KEYS TERMINATED BY char] [LINES TERMINATED BY char]
[NULL DEFINED AS char] -- (Note: Available in Hive 0.13 and later)
| SERDE serde_name [WITH SERDEPROPERTIES (property_name=property_value, property_name=property_value, ...)]

file_format:
: SEQUENCEFILE
| TEXTFILE -- (Default, depending on hive.default.fileformat configuration)
| RCFILE -- (Note: Available in Hive 0.6.0 and later)
| ORC -- (Note: Available in Hive 0.11.0 and later)
| PARQUET -- (Note: Available in Hive 0.13.0 and later)
| AVRO -- (Note: Available in Hive 0.14.0 and later)
| JSONFILE -- (Note: Available in Hive 4.0.0 and later)
| INPUTFORMAT input_format_classname OUTPUTFORMAT output_format_classname

column_constraint_specification:
: [ PRIMARY KEY|UNIQUE|NOT NULL|DEFAULT [default_value]|CHECK [check_expression] ENABLE|DISABLE NOVALIDATE RELY/NORELY ]

default_value:
: [ LITERAL|CURRENT_USER()|CURRENT_DATE()|CURRENT_TIMESTAMP()|NULL ]

constraint_specification:
: [, PRIMARY KEY (col_name, ...) DISABLE NOVALIDATE RELY/NORELY ]
[, PRIMARY KEY (col_name, ...) DISABLE NOVALIDATE RELY/NORELY ]
[, CONSTRAINT constraint_name FOREIGN KEY (col_name, ...) REFERENCES table_name(col_name, ...) DISABLE NOVALIDATE
[, CONSTRAINT constraint_name UNIQUE (col_name, ...) DISABLE NOVALIDATE RELY/NORELY ]
[, CONSTRAINT constraint_name CHECK [check_expression] ENABLE|DISABLE NOVALIDATE RELY/NORELY ]
  • CREATE TABLE 创建一个指定名字的表。如果相同名字的表已经存在,则抛出异常,可使用 IF NOT EXISTS 选项来忽略这个异常

  • EXTERNAL 该关键字可以让用户创建一个外部表,在创建表的同时指定一个指向实际数据的路径(LOCATION),Hive 创建内部表时,会将数据移动到数据仓库指向的路径;若创建外部表,仅记录数据所在的路径,不对数据的位置做任何改变。在删除表的时候,内部表的元数据和数据会被一起删除,而外部表只删除元数据,不删除数据

  • TEMPORARY 表示是临时表,在当前会话内,这张表有效,当会话结束,则这张表失效。EXTERNAL 表示是外部表,在建表的同时指定一个指向实际数据的路径。删除的表的时候,只会删除元数据,不会删除表数据

  • COMMENT :可以为表与字段增加描述

  • ROW FORMAT :用户在建表的时候可以自定义 SerDe 或者使用自带的 SerDe

  • PARTITIONED BY 表示按什么字段来分区

  • CLUSTERED BY 表示分桶表,按什么字段分区和排序。INTO 表示根据这个字段分多少个桶。

  • SKEWED BY 表示指定某些列上有倾斜值,Hive 会记录下这些值,在查询的时候,会有更好的性能表现

  • STORED AS 表示以什么压缩格式来存储,如果文件数据是纯文本,则使用 STORED AS TEXTFILE ;如果数据需要压缩, 则使用 STORED AS SEQUENCE

  • LIKE: 允许用户复制现有的表结构,但是不复制数据

存储格式

Hive 会在 HDFS 为每个数据库上创建一个目录,数据库中的表是该目录的子目录,表中的数据会以文件的形式存储在对应的表目录下。Hive 支持以下几种文件存储格式:

格式 说明
TextFile 存储为纯文本文件。 这是 Hive 默认的文件存储格式。这种存储方式数据不做压缩,磁盘开销大,数据解析开销大。
SequenceFile SequenceFile 是 Hadoop API 提供的一种二进制文件,它将数据以<key,value>的形式序列化到文件中。这种二进制文件内部使用 Hadoop 的标准的 Writable 接口实现序列化和反序列化。它与 Hadoop API 中的 MapFile 是互相兼容的。Hive 中的 SequenceFile 继承自 Hadoop API 的 SequenceFile,不过它的 key 为空,使用 value 存放实际的值,这样是为了避免 MR 在运行 map 阶段进行额外的排序操作。
RCFile RCFile 文件格式是 FaceBook 开源的一种 Hive 的文件存储格式,首先将表分为几个行组,对每个行组内的数据按列存储,每一列的数据都是分开存储。
ORC Files ORC 是在一定程度上扩展了 RCFile,是对 RCFile 的优化。
Avro Files Avro 是一个数据序列化系统,设计用于支持大批量数据交换的应用。它的主要特点有:支持二进制序列化方式,可以便捷,快速地处理大量数据;动态语言友好,Avro 提供的机制使动态语言可以方便地处理 Avro 数据。
Parquet Parquet 是基于 Dremel 的数据模型和算法实现的,面向分析型业务的列式存储格式。它通过按列进行高效压缩和特殊的编码技术,从而在降低存储空间的同时提高了 IO 效率。

以上压缩格式中 ORC 和 Parquet 的综合性能突出,使用较为广泛,推荐使用这两种格式。

通常在创建表的时候使用 STORED AS 参数指定:

1
2
3
4
5
6
7
CREATE TABLE page_view(viewTime INT, userid BIGINT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\001'
COLLECTION ITEMS TERMINATED BY '\002'
MAP KEYS TERMINATED BY '\003'
STORED AS SEQUENCEFILE;
复制代码

各个存储文件类型指定方式如下:

  • STORED AS TEXTFILE
  • STORED AS SEQUENCEFILE
  • STORED AS ORC
  • STORED AS PARQUET
  • STORED AS AVRO
  • STORED AS RCFILE

内容格式

当数据存储在文本文件中,必须按照一定格式区别行和列,如使用逗号作为分隔符的 CSV 文件 (Comma-Separated Values) 或者使用制表符作为分隔值的 TSV 文件 (Tab-Separated Values)。但此时也存在一个缺点,就是正常的文件内容中也可能出现逗号或者制表符。

所以 Hive 默认使用了几个平时很少出现的字符,这些字符一般不会作为内容出现在文件中。Hive 默认的行和列分隔符如下表所示。

分隔符 描述
\n 对于文本文件来说,每行是一条记录,所以可以使用换行符来分割记录
^A (Ctrl+A) 分割字段 (列),在 CREATE TABLE 语句中也可以使用八进制编码 \001 来表示
^B 用于分割 ARRAY 或者 STRUCT 中的元素,或者用于 MAP 中键值对之间的分割, 在 CREATE TABLE 语句中也可以使用八进制编码 \002 表示
^C 用于 MAP 中键和值之间的分割,在 CREATE TABLE 语句中也可以使用八进制编码 \003 表示

使用示例如下:

1
2
3
4
5
6
CREATE TABLE page_view(viewTime INT, userid BIGINT)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\001'
COLLECTION ITEMS TERMINATED BY '\002'
MAP KEYS TERMINATED BY '\003'
STORED AS SEQUENCEFILE;

内部表和外部表

内部表又叫做管理表 (Managed/Internal Table),创建表时不做任何指定,默认创建的就是内部表。想要创建外部表 (External Table),则需要使用 External 进行修饰。 内部表和外部表主要区别如下:

内部表 外部表
数据存储位置 内部表数据存储的位置由 hive.metastore.warehouse.dir 参数指定,默认情况下表的数据存储在 HDFS 的 /user/hive/warehouse/数据库名.db/表名/ 目录下 外部表数据的存储位置创建表时由 Location 参数指定;
导入数据 在导入数据到内部表,内部表将数据移动到自己的数据仓库目录下,数据的生命周期由 Hive 来进行管理 外部表不会将数据移动到自己的数据仓库目录下,只是在元数据中存储了数据的位置
删除表 删除元数据(metadata)和文件 只删除元数据(metadata)

内部表

也称作管理表,Hive默认情况会将表数据放在hive.metastore.warehouse.dir定义的目录当中,删除表,表数据也会一起删除

  • 普通创建表
1
2
3
4
5
create table if not exists student2(
id int, name string)
row format delimited fields terminated by '\t'
stored as textfile
location '/user/hive/warehouse/student2';
  • 根据查询结果创建表(查询的结果会添加到新创建的表中)-实际常用
1
create table if not exists student3 as select id, name from student;
  • 根据已经存在的表结构创建表
1
create table if not exists student4 like student;
  • 查询表的类型
1
2
hive (hive_test)> desc formatted student2;
Table Type: MANAGED_TABLE

外部表

表数据可以放置在任意位置,删除表,不会删除表数据,但是会删除表元数据信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 创建外部表
hive (hive_test)> create external table test_part_ch (
> id int,
> name STRING,
> sex STRING,
> age INT,
> birthday DATE
> )
> ROW FORMAT DELIMITED FIELDS TERMINATED BY "\t"
> LOCATION '/tmp/test_cn.txt';
OK
Time taken: 0.259 seconds

# 查看表数据
hive (hive_test)> select * from test_part_ch;
OK
test_part_ch.id test_part_ch.name test_part_ch.sex test_part_ch.age test_part_ch.birthday
1 a 男 22 NULL
2 b 男 23 NULL
3 c 男 24 NULL
Time taken: 0.373 seconds, Fetched: 3 row(s)

内部表和外部表互转

  • 查询表的类型
1
2
hive (hive_test)> desc formatted student2;
Table Type: MANAGED_TABLE
  • 修改内部表student2为外部表
1
alter table student2 set tblproperties('EXTERNAL'='TRUE');
  • 查询表的类型
1
2
hive (hive_test)> desc formatted student2;
Table Type: EXTERNAL_TABLE
  • 修改外部表student2为内部表
1
alter table student2 set tblproperties('EXTERNAL'='FALSE');
  • 查询表的类型
1
2
hive (hive_test)> desc formatted student2;
Table Type: MANAGED_TABLE

注意:(‘EXTERNAL’=’TRUE’)和(‘EXTERNAL’=’FALSE’)为固定写法,区分大小写!

分区表

Hive 中的表对应为 HDFS 上的指定目录,在查询数据时候,默认会对全表进行扫描,这样时间和性能的消耗都非常大。

分区为 HDFS 上表目录的子目录,数据按照分区存储在子目录中。如果查询的 where 字句的中包含分区条件,则直接从该分区去查找,而不是扫描整个表目录,合理的分区设计可以极大提高查询速度和性能。

通常,在管理大规模数据集的时候都需要进行分区,比如将日志文件按天进行分区,从而保证数据细粒度的划分,使得查询性能得到提升。

在 Hive 中可以使用 PARTITIONED BY 子句创建分区表。表可以包含一个或多个分区列,程序会为分区列中的每个不同值组合创建单独的数据目录。

注意:分区字段不能是表中已经存在的数据,可以将分区字段看作表的伪列。

下面的我们创建一张表测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 创建表
hive (hive_test)> CREATE EXTERNAL TABLE test_part(
> id INT,
> name STRING,
> sex STRING,
> age INT,
> birthday DATE
> )
> PARTITIONED BY (country STRING) -- 按照国家进行分区
> ROW FORMAT DELIMITED FIELDS TERMINATED BY "\t"
> LOCATION '/user/hive/warehouse/test_part';
OK
Time taken: 0.975 seconds
# 查看表定义
hive (hive_test)> show tables;
OK
tab_name
test_part
Time taken: 0.038 seconds, Fetched: 1 row(s)
hive (hive_test)> desc test_part;
OK
col_name data_type comment
id int
name string
sex string
age int
birthday date
country string

# Partition Information
# col_name data_type comment

country string
Time taken: 0.895 seconds, Fetched: 11 row(s)

创建后查看HDFS信息,如下:

image-20220517161941306

加载数据入表

提前准备两份数据在本地目录:

1
2
3
4
5
6
7
8
9
10
[hadoop@node1 tmp]$ pwd
/app/tmp
[hadoop@node1 tmp]$ cat test_cn.txt
1 a 男 22 1989/8/15
2 b 男 23 1989/8/15
3 c 男 24 1989/8/15
[hadoop@node1 tmp]$
[hadoop@node1 tmp]$ cat test_jp.txt
4 d 女 25 1989/8/15
5 e 女 26 1989/8/15

将数据上传到HDFS上:

1
2
[hadoop@node1 tmp]$ hdfs dfs -put ./test_cn.txt /tmp
[hadoop@node1 tmp]$ hdfs dfs -put ./test_jp.txt /tmp

加载数据入创建的test_part表中(注意:分区表加载数据时,必须指定分区):

1
2
3
4
5
6
7
8
9
hive (hive_test)> load data inpath '/tmp/test_cn.txt' into table hive_test.test_part partition(country='cn');
Loading data to table hive_test.test_part partition (country=cn)
OK
Time taken: 2.083 seconds

hive (hive_test)> load data inpath '/tmp/test_jp.txt' into table hive_test.test_part partition(country='jp');
Loading data to table hive_test.test_part partition (country=jp)
OK
Time taken: 1.205 seconds

Hive Load语句不会在加载数据的时候做任何转换工作,而是纯粹的把数据文件复制/移动到Hive表对应的地址。语法格式如下:

1
2
3
4
5
6
LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename \
[PARTITION (partcol1=val1, partcol2=val2 ...)]

LOAD DATA [LOCAL] INPATH 'filepath' [OVERWRITE] INTO TABLE tablename \
[PARTITION (partcol1=val1, partcol2=val2 ...)] \
[INPUTFORMAT 'inputformat' SERDE 'serde'] (3.0 or later)

几点说明:

  • 如果命令中带有LOCAL,说明从本地文件系统加载数据,文件路径可以是相对路径,也可以是绝对路径。在这种情况下,首先将文件从本地复制到hdfs相应的位置,然后移动到hive表格中,这个时候原始数据文件是存在于Hive表之下的路径下。
  • 如果不包含LOCAL关键字,则移动HDFS文件到目标表中。
  • filepath 可以是一个相对路径,也可以是一个绝对路径。可以是一个文件,也可以是一个文件夹目录。如果是一个目录,这个时候文件夹下的所有文件都会被加载。
  • 命令中如果带有overwirte,代表加载数据之前会清空目标表格,否则就是追加的方式。
  • 如果表是分区表则必须指定PARTITION从句,否则会报如下错误:
    FAILED:SemanticException [Error 10062]: Need to specify partition columns because the destination table is partitioned

再次查看HDFS上的文件:

1
2
3
4
5
6
7
8
9
[hadoop@node1 ~]$ hdfs dfs -ls /user/hive/warehouse/
Found 2 items
drwxr-xr-x - hadoop supergroup 0 2022-05-16 16:18 /user/hive/warehouse/hive_test.db
drwxr-xr-x - hadoop supergroup 0 2022-05-17 16:37 /user/hive/warehouse/test_part
[hadoop@node1 ~]$
[hadoop@node1 ~]$ hdfs dfs -ls /user/hive/warehouse/test_part
Found 2 items
drwxr-xr-x - hadoop supergroup 0 2022-05-17 16:37 /user/hive/warehouse/test_part/country=cn
drwxr-xr-x - hadoop supergroup 0 2022-05-17 16:37 /user/hive/warehouse/test_part/country=jp

管理分区

  • 创建单个分区
1
hive (hive_test)> alter table test_part add partition(month='201706') ;
  • 同时创建多个分区
1
hive (hive_test)> alter table test_part add partition(month='201705') partition(month='201704');
  • 删除单个分区
1
hive (hive_test)> alter table test_part drop partition (month='201704');
  • 同时删除多个分区
1
hive (hive_test)> alter table test_part drop partition (month='201705'), partition (month='201706');

7.查看分区表有多少分区

1
2
3
4
5
6
hive (hive_test)> show partitions test_part;
OK
partition
country=cn
country=jp
Time taken: 0.244 seconds, Fetched: 2 row(s)

分区表和数据产生关联

把数据直接上传到分区目录上,让分区表和数据产生关联的三种方式

  • 方式一:上传数据后修复

上传数据

1
2
3
4
5
hive (default)> dfs -mkdir -p
/user/hive/warehouse/dept_partition2/month=201709/day=12;
hive (default)> dfs -put /opt/module/datas/dept.txt
/user/hive/warehouse/dept_partition2/month=201709/day=12;
复制代码

查询数据(查询不到刚上传的数据)

1
2
3
4
hive (default)> select * from dept_partition2 where month='201709' and day='12';
执行修复命令
hive> msck repair table dept_partition2;
复制代码

再次查询数据

1
2
hive (default)> select * from dept_partition2 where month='201709' and day='12';
复制代码
  • 方式二:上传数据后添加分区

上传数据

1
2
hive (default)> dfs -mkdir -p /user/hive/warehouse/dept_partition2/month=201709/day=11;
hive (default)> dfs -put /opt/module/datas/dept.txt /user/hive/warehouse/dept_partition2/month=201709/day=11;

执行添加分区

1
2
3
hive (default)> alter table dept_partition2 add partition(month='201709',
day='11');
复制代码

查询数据

1
2
hive (default)> select * from dept_partition2 where month='201709' and day='11';
复制代码
  • 方式三:创建文件夹后load数据到分区

创建目录

1
hive (default)> dfs -mkdir -p /user/hive/warehouse/dept_partition2/month=201709/day=10;

上传数据

1
hive (default)> load data local inpath '/opt/module/datas/dept.txt' into table dept_partition2 partition(month='201709',day='10');

查询数据

1
hive (default)> select * from dept_partition2 where month='201709' and day='10';

分桶表

分区提供了一个隔离数据和优化查询的可行方案,但是并非所有的数据集都可以形成合理的分区,分区的数量也不是越多越好,过多的分区条件可能会导致很多分区上没有数据。同时 Hive 会限制动态分区可以创建的最大分区数,用来避免过多分区文件对文件系统产生负担。鉴于以上原因,Hive 还提供了一种更加细粒度的数据拆分方案:分桶表 (bucket Table)。

分桶表会将指定列的值进行哈希散列,并对 bucket(桶数量)取余,然后存储到对应的 bucket(桶)中。

在 Hive 中,我们可以通过 CLUSTERED BY 指定分桶列,并通过 SORTED BY 指定桶中数据的排序参考列。下面为分桶表建表语句示例:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE EXTERNAL TABLE emp_bucket(
empno INT,
ename STRING,
job STRING,
mgr INT,
hiredate TIMESTAMP,
sal DECIMAL(7,2),
comm DECIMAL(7,2),
deptno INT)
CLUSTERED BY(empno) SORTED BY(empno ASC) INTO 4 BUCKETS --按照员工编号散列到四个 bucket 中
ROW FORMAT DELIMITED FIELDS TERMINATED BY "\t"
LOCATION '/hive/emp_bucket';

Hive分桶的特点

分桶的基本原理是分桶列的hash_function%mod = bucketId。

可以看到前提是,我们要指定mod,也即是分桶的个数,其实该值也是运行的最大reduce个数。

分桶的特征如下:

  1. hash_function取决于bucketing列的类型。
  2. 具有相同分段列的记录将始终存储在同一个桶中。
  3. 使用CLUSTERED BY将表分成桶。
  4. 通常,在表目录中,每个存储桶只是一个文件,并且存储桶编号是从1开始的。
  5. 可以先分区再分桶,也可以直接分桶。
  6. 此外,Bucketed表将创建几乎相等的分布式数据文件块(取决于分桶列是否离散)。

hive分桶的优势

  1. 与非分桶表相比,分桶表提供了高效采样。通过采样,我们可以尝试对一小部分数据进行查询,以便在原始数据集非常庞大时进行测试和调试。
  2. 由于数据文件是相同大小的部分,map-side join在分桶表上执行的速度比分区表块很多。在map-side join时,处理左侧表的map知道要匹配的右表中的行在相关的桶中,因此只需要检索该桶。
  3. 分桶表查询速度快于非分桶表。
  4. Bucketing概念还提供了灵活性,可以使每个存储桶中的记录按一列或多列进行排序。 这使得map-side join更加高效,因为每个存储桶的join变为高效的合并排序(merge-sort)。

hive分桶的缺点

指定bucketing并不能确保正确填充表。 数据加载到存储桶需要由我们自己处理。

举个例子

创建分区分桶表

比如创建一个表,按照国家分区,州分桶,然后对城市进行升序排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CREATE TABLE bucketed_user(
firstname VARCHAR(64),
lastname VARCHAR(64),
address STRING,
city VARCHAR(64),
state VARCHAR(64),
post STRING,
phone1 VARCHAR(64),
phone2 STRING,
email STRING,
web STRING
)
COMMENT 'A bucketed sorted user table'
PARTITIONED BY (country VARCHAR(64))
CLUSTERED BY (state) SORTED BY (city) INTO 32 BUCKETS
STORED AS SEQUENCEFILE;

插入数据

与分区表类似,我们不能直接使用LOAD DATA(LOCAL)INPATH命令加载数据到分桶表,直接使用 Load 语句向分桶表加载数据,数据时可以加载成功的,但是数据并不会分桶。

这是由于分桶的实质是对指定字段做了 hash 散列然后存放到对应文件中,这意味着向分桶表中插入数据是必然要通过 MapReduce,且 Reducer 的数量必须等于分桶的数量。由于以上原因,分桶表的数据通常只能使用 CTAS(CREATE TABLE AS SELECT) 方式插入,因为 CTAS 操作会触发 MapReduce。

使用INSERT OVERWRITE TABLE … SELECT … FROM子句来填充分桶表。 为此,我们将在hive中创建一个临时表,其中包含该表中输入文件中的所有列,我们将复制到目标bucketed表中。

假设我们已经创建了temp_user临时表,下面是用于使用temp_user表填充分桶表的HiveQL。

要填充分桶表,我们需要设置属性hive.enforce.bucketing = true,以便Hive知道创建表定义中声明的桶数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
set hive.enforce.bucketing = true;

INSERT OVERWRITE TABLE bucketed_user PARTITION (country)
SELECT firstname ,
lastname ,
address ,
city ,
state ,
post ,
phone1 ,
phone2 ,
email ,
web ,
country
FROM temp_user;

注意:

属性hive.enforce.bucketing = true类似于分区中的hive.exec.dynamic.partition = true属性。 通过设置此属性,我们将在将数据加载到hive表时启用动态分桶。

它会自动将reduce任务的数量设置为等于表定义中提到的桶数(例如,在我们的例子中为32),并自动从表定义中选择clustered by列。

如果我们不在Hive Session中设置此属性,我们必须手动将相同的信息在上面的INSERT … SELECT语句结尾处传递给Hive,也即要运行的reduce任务的数量(例如在我们的例子中,通过set mapred.reduce.tasks = 32)和CLUSTER BY (state)还有SORT BY(city)子句。

完整的sql语句如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
set hive.exec.dynamic.partition=true;
set hive.exec.dynamic.partition.mode=nonstrict;
set hive.exec.max.dynamic.partitions.pernode=1000;
set hive.enforce.bucketing = true;

DROP TABLE IF EXISTS bucketed_user;

CREATE TEMPORARY TABLE temp_user(
firstname VARCHAR(64),
lastname VARCHAR(64),
address STRING,
country VARCHAR(64),
city VARCHAR(64),
state VARCHAR(64),
post STRING,
phone1 VARCHAR(64),
phone2 STRING,
email STRING,
web STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
STORED AS TEXTFILE;

LOAD DATA LOCAL INPATH '/home/user/user_table.txt' INTO TABLE temp_user;

CREATE TABLE bucketed_user(
firstname VARCHAR(64),
lastname VARCHAR(64),
address STRING,
city VARCHAR(64),
state VARCHAR(64),
post STRING,
phone1 VARCHAR(64),
phone2 STRING,
email STRING,
web STRING
)
COMMENT 'A bucketed sorted user table'
PARTITIONED BY (country VARCHAR(64))
CLUSTERED BY (state) SORTED BY (city) INTO 32 BUCKETS
STORED AS SEQUENCEFILE;

set hive.enforce.bucketing = true;
INSERT OVERWRITE TABLE bucketed_user PARTITION (country)
SELECT firstname ,
lastname ,
address ,
city ,
state ,
post ,
phone1 ,
phone2 ,
email ,
web ,
country
FROM temp_user;

如何确定分桶数?分桶数的确定要结合和两点:

  1. 分桶的列基数要大,也即是该列去重后的值要大。

  2. 每个桶数据文件不能太小也不能太大。比如,如果block大小是256MB,那么使每个桶512 MB,是个不错的选择。

强调一下,为了正确的加载数据,需要将reduce数目和分桶数一样。设置方法如上。


蚂蚁🐜再小也是肉🥩!


Hive常用操作
http://heibanbai.com.cn/posts/e2dcb65e/
作者
黑伴白
发布于
2022年5月31日
许可协议

“您的支持,我的动力!觉得不错的话,给点打赏吧 ୧(๑•̀⌄•́๑)૭”

微信二维码

微信支付

支付宝二维码

支付宝支付