Hive 分区和存储桶示例

表、分区和桶是 Hive 数据建模的组成部分。表定义了数据模式,分区将表拆分为磁盘上的多个目录,而桶则将目录中的数据拆分为固定数量的文件。
什么是分区?
Hive 分区是一种将表组织成分区的方法,它根据分区键将表划分为不同的部分。从物理层面来说,每个分区都是 HDFS 中表文件夹下的一个独立子目录,这使得 Hive 可以跳过不需要的数据。
当表包含一个或多个分区键时,分区就显得尤为重要。分区键是决定数据在表中存储方式的基本元素。分区键本身并不存储在数据文件中,它的值编码在目录名称中,因此,基于该键的查询筛选可以在读取任何行之前丢弃整个目录。这称为分区修剪。
例如: -
“客户拥有一些电子商务数据,这些数据属于印度业务,其中涵盖了印度38个邦的业务。如果我们以邦/州列作为分区键,并对这些印度数据进行分区,我们将得到38个分区,这与印度的邦/州数量(38)相等。这样,每个邦/州的数据就可以在分区表中单独查看。”
样本 Code 分区代码片段
以下六条语句在 Hive shell 中按顺序执行。每条语句都是一个独立的步骤,后面的屏幕截图显示了在运行中的集群上执行相同序列的过程。
- 创建表 allstates
create table allstates(state string, District string,Enrolments string) row format delimited fields terminated by ',';
- 将数据加载到创建的表 allstates 中
Load data local inpath '/home/hduser/Desktop/AllStates.csv' into table allstates;
- 分区表的创建
create table state_part(District string,Enrolments string) PARTITIONED BY(state string);
- 对于分区,我们必须设置此属性
set hive.exec.dynamic.partition.mode=nonstrict - 将数据加载到分区表中
INSERT OVERWRITE TABLE state_part PARTITION(state) SELECT district,enrolments,state from allstates;
- 基于状态作为分区键的分区表的实际处理和形成
HDFS 存储中将有 38 个分区输出,文件名即为状态名称。我们将在此步骤中进行检查。
以下屏幕截图显示了上述代码的执行情况。
在第一个屏幕中,步骤 1 运行于 蜂巢> 提示并创建 所有州 表格包含三列,字段分隔符为逗号。
下一个屏幕涵盖步骤 2 和 3:将 AllStates.csv 文件加载到 所有州以及分区表 州/省 创建时,状态被声明为分区键。
接下来是步骤 4 和 5。动态分区模式设置为非严格模式,INSERT OVERWRITE 语句启动一个 MapReduce 作业,其日志显示每个状态值对应加载一个分区。
在 HDFS 中列出仓库目录证实了步骤 6 的结果——shell 报告有 38 个项目,其中一个 州/州= 按州划分目录。
从上面的代码中,我们做以下事情
- 创建名为 allstates 的表,包含三列,列名分别为 state、district 和 enrolments。
- 将数据加载到表 allstates 中
- 创建以状态为分区键的分区表
- 在此步骤中,将分区模式设置为非严格模式(此模式将激活动态分区模式)。
- 将数据加载到分区表 state_part 中
- 基于状态作为分区键的分区表的实际处理和形成
- HDFS 存储中将有 38 个分区输出,文件名作为状态名称。在此步骤中,我们将看到 HDFS 中的这 38 个分区输出。
Hive中的静态分区与动态分区
上面的示例使用了动态分区,但 Hive 支持两种加载方式,两者的区别决定了加载多少数据。ping ——以及每批货物所携带的风险。
静态分区 语句本身指定了分区值,因此必须在加载运行之前知道该值,并且一条语句只能填充一个分区。 动态分区 让 Hive 从 SELECT 列表的最后一列读取分区值,并在运行时创建目录,这就是为什么该示例只需要一次 INSERT 操作即可生成 38 个目录。
| 方面 | 静态分区 | 动态分区 |
|---|---|---|
| 分区值 | 在“分割条款”中手工提供 | 运行时从 SELECT 列读取 |
| 每个语句的分区数 | 一个 | 更多来自Google的 |
| 配置 | 在默认严格模式下运行 | 需要将 hive.exec.dynamic.partition.mode 设置为 nonstrict。 |
| 加载速度 | 速度更快,因为无需进行数值扫描。 | 速度较慢,因为该作业按键对行进行分组。 |
| 最适合 | 小的、已知的集合,例如每日负荷 | 大型或未知密钥集,例如 38 个州 |
动态负载也有上限。Hive 限制单个作业可以创建的分区数量——默认情况下,每个 mapper 或 reducer 最多 100 个分区,整个语句最多 1000 个分区——一旦超过上限,作业就会失败。因此,对于基数非常高的键,需要提高这些限制或采用不同的设计。
什么是 Bucket?
Hive 中的存储桶用于将 Hive 表中的数据隔离到多个文件或目录中。它们用于提高查询效率,并且与分区不同,存储桶的数量在创建表时就已固定,因此不会随数据增长而增加。
- 这些分区中的数据可以进一步划分成多个桶。
- 该分割操作是基于我们在表中选择的特定列的哈希值进行的。
- 存储桶在后端使用某种哈希算法来读取每条记录并将其放入不同的存储桶中。
- 一行最终落入哪个桶是由……决定的 hash_function(bucketing_column) mod num_buckets因此,相等的值总是保存在同一个文件中。
- 在 Hive 0.x 和 1.x 版本中,必须启用分桶功能。 设置 hive.enforce.bucketing=true; 插入前
最后那个设置是当前集群上的历史设置: Apache Hive 手册 需要注意的是,从 Hive 2.x 开始就不需要这样做了,因为引擎现在会自动从表定义中获取 reducer 计数和 cluster-by 列。
步骤1) 如下所示创建Bucket。
下图显示了 CREATE TABLE 语句 样本桶其中,底部的 CLUSTERED BY 子句用于确定桶的数量。
从上面的屏幕截图
- 我们正在创建一个名为 samplebucket 的示例存储桶,其中包含 first_name、job_id、department、salary 和 country 等列名。
- 我们在这里创建了4个桶。
- 数据加载完成后,它会自动将数据放入 4 个桶中。
- 国家/地区列是聚类列,因此每个国家/地区的每一行都会写入同一个存储桶文件。
步骤2) 将数据加载到表 samplebucket
假设 Hive 系统中已经创建了“employees”表,在此步骤中,我们将看到数据从 employees 表加载到 samplebucket 表中。
在开始将员工数据移入存储桶之前,请确保数据包含诸如 first_name、job_id、department、salary 和 country 之类的列名。
这里我们将员工表中的数据加载到 samplebucket 中——下面的屏幕显示了执行复制操作的 INSERT OVERWRITE 语句。
步骤3) 显示步骤 1 中创建的 4 个桶
列出 HDFS 中的表目录显示了实际结果:四个编号的数据文件,而不是一个。
从上面的截图中我们可以看到,员工表中的数据被转移到了步骤 1 中创建的 4 个桶中。
Hive分区与分桶:主要区别
这两个功能都能将表格分割成更小的部分,但它们在文件系统的不同层级上进行操作,并且解决的问题也不同。下表对它们进行了对比。
| 比较点 | 分区 | 桶装 |
|---|---|---|
| 单位已创建 | 每个键值对应一个目录 | 每个哈希桶一个文件 |
| 已声明 | 分区 | 按…聚类成 n 个桶 |
| 件数 | 随着不同值的数量增加而增长 | 在创建表时修复 |
| 存储在数据文件中的列 | 不——值存在于目录名称中。 | 是的——该列仍然是普通的列。 |
| 最佳列类型 | 低基数,例如州、年份或国家 | 高基数,例如 user_id 或 transaction_id |
| 主要好处 | 分区修剪会跳过不需要的目录 | 甚至文件大小、低成本采样和地图边连接 |
两者并非竞争关系。常见的生产布局是将事实表按日期分区,然后根据连接键对每天的数据进行分组,这样查询只需将数据精简到一个目录中,然后逐个连接该目录中的数据即可。
何时使用分区、分桶或两者都用
选择哪种方式首先要考虑列的基数以及读取表的查询的结构。
- 隔墙 当查询几乎总是基于同一个低基数列进行筛选,并且不同值的数量保持在几百而不是几百万时,就会出现问题。
- 桶 当有用列的不同值过多而无法作为目录时,或者当表在该列上进行重复连接或采样时,
- 同时使用 对于大型事实表:按日期分区,然后在每个分区内按连接键进行分组。
需要警惕的故障模式是小文件问题。基于基数非常高的列(例如时间戳或客户标识符)进行分区,会生成数千个小目录,每个目录包含的文件远小于 HDFS 块大小。这会导致 NameNode 内存占用增加,每次扫描速度变慢,而分区的初衷正是为了避免这种情况。分桶可以避免这个问题,因为文件数量受到表定义的限制。
分桶机制本身也存在一个问题:只有当所有写入操作都遵循分桶布局时,布局才能正确。创建时声明的桶计数是元数据,因此,如果作业在写入表时没有正确进行分桶,则可能会留下与声明布局不匹配的文件,导致后续的采样或桶连接操作读取错误的行。







