在 Hive 中对表进行分区和分桶有啥区别?

Posted

技术标签:

【中文标题】在 Hive 中对表进行分区和分桶有啥区别?【英文标题】:What is the difference between partitioning and bucketing a table in Hive ?在 Hive 中对表进行分区和分桶有什么区别? 【发布时间】:2013-10-08 08:46:42 【问题描述】:

我知道两者都是在表中的列上执行的,但是每个操作有何不同。

【问题讨论】:

【参考方案1】:

分区数据通常用于水平分布负载,这具有性能优势,并有助于以逻辑方式组织数据。 示例:如果我们正在处理一个大型 employee 表,并且经常运行带有 WHERE 子句的查询,这些子句将结果限制在特定的国家或部门。为了更快的查询响应 Hive 表可以是PARTITIONED BY (country STRING, DEPT STRING)。分区表改变了 Hive 构建数据存储的方式,Hive 现在将创建反映分区结构的子目录,例如

.../employees/country=ABC/DEPT=XYZ.

如果对来自country=ABC的员工进行查询限制,它只会扫描一个目录country=ABC的内容。这可以显着提高查询性能,但前提是分区方案反映了通用过滤。分区功能在 Hive 中非常有用,但是,创建过多分区的设计可能会优化某些查询,但对其他重要查询不利。分区过多的另一个缺点是创建了大量不必要的 Hadoop 文件和目录,并增加了 NameNode 的开销,因为它必须将文件系统的所有元数据保存在内存中。

Bucketing 是另一种将数据集分解为更易于管理的部分的技术。例如,假设一张表使用date作为***分区,employee_id作为二级分区,导致小分区过多。相反,如果我们对雇员表进行分桶并使用employee_id 作为分桶列,则该列的值将按用户定义的数字散列到桶中。具有相同employee_id 的记录将始终存储在同一个存储桶中。假设employee_id的数量远大于桶的数量,那么每个桶会有很多employee_id。创建表时,您可以指定 CLUSTERED BY (employee_id) INTO XX BUCKETS; ,其中 XX 是桶数。分桶有几个优点。桶的数量是固定的,因此它不会随数据波动。如果两个表被employee_id 分桶,Hive 可以创建逻辑上正确的采样。分桶还有助于进行高效的地图侧连接等。

【讨论】:

感谢 Navneet。但是,您能否详细说明分区是如何进行分桶的?假设如果我们在 CLUSED BY 子句中指定了 32 个桶,并且 CREATE TABLE 语句也包含了 Partitioning 子句,那么分区和桶将如何一起管理?分区数是否限制为 32?或者对于每个分区,将创建 32 个存储桶?每个存储桶都是 HDFS 文件吗? Hive 表可以同时具有分区和分桶。根据您的分区子句,每个分区将创建 32 个存储桶。是 HDFS 文件。 @sgsi 分区是文件夹,桶是文件。 为了记录,这个答案来自 Programming Hive (O'Reilly, 2012) 的文本。 我发现这个链接很有用。它的信息将为该答案增加更多价值。 linkedin.com/pulse/…【参考方案2】:

前面的解释中缺少一些细节。 为了更好地理解分区和分桶的工作原理,您应该查看数据是如何存储在 hive 中的。 假设你有一张桌子

CREATE TABLE mytable ( 
         name string,
         city string,
         employee_id int ) 
PARTITIONED BY (year STRING, month STRING, day STRING) 
CLUSTERED BY (employee_id) INTO 256 BUCKETS

然后 hive 会将数据存储在目录层次结构中,例如

/user/hive/warehouse/mytable/y=2015/m=12/d=02

因此,分区时必须小心,因为例如,如果您按employee_id 分区并且拥有数百万员工,那么您的文件系统中最终将拥有数百万个目录。 术语“cardinality”是指一个字段可以具有的可能值的数量。例如,如果你有一个“国家”字段,世界上的国家大约有 300 个,所以基数约为 300。对于像“timestamp_ms”这样的字段,每毫秒都会改变一次,基数可能是数十亿。一般来说,在选择分区的字段时,它不应该有很高的基数,因为你的文件系统中的目录太多了。

另一方面,集群又名分桶,将产生固定数量的文件,因为您确实指定了桶的数量。 hive 将做的是获取字段,计算哈希并将记录分配给该存储桶。 但是,如果您使用 256 个存储桶并且您要存储的字段的基数较低(例如,它是美国的一个州,所以只能有 50 个不同的值)会发生什么?您将有 50 个包含数据的存储桶和 206 个没有数据的存储桶。

有人已经提到分区如何显着减少您查询的数据量。因此,在我的示例表中,如果您只想从某个日期开始查询,那么按年/月/日进行分区将大大减少 IO 量。 我认为有人还提到了分桶如何加快与具有完全相同分桶的其他表的连接,因此在我的示例中,如果您要在同一个employee_id 上连接两个表,hive 可以做到逐桶加入(如果它们已经按employee_id 排序更好,因为它将对已经排序的部分进行合并排序,这在线性时间内工作,也就是O(n))。

因此,当字段具有高基数并且数据在存储桶之间均匀分布时,分桶效果很好。当分区字段的基数不太高时,分区效果最好。

此外,您可以在多个字段上进行分区,按顺序(年/月/日就是一个很好的例子),而您只能在一个字段上进行分区

【讨论】:

您能否在示例中解释一下 SORTED-BY 的 CLUSTERED-BY 行为?根据我的例子,我发现 SORTED-BY 什么也没做。我有什么遗漏吗? CLUSTERED BY x,y 就像写 DISTRIBUTE BY x,y SORT BY x,y (请参阅cwiki.apache.org/confluence/display/Hive/…)所以将 SORT BY 添加到 CLUSTERED BY 没有任何效果。 有趣,我同意选择查询中的用法。但想知道为什么人们在表创建语句中同时使用 clustered by 和 sorted by。如果 DDL 中 SORTED BY 没有意义,那么为什么会出现这个关键字?没听懂。 SORTED BY 旨在与 DISTRIBUTED BY 一起使用。例如,您可能希望在存储桶中按用户 ID 分配并按时间排序。 CLUSTER BY 只是 SORTED BY 和 DISTRIBUTED BY 上的子句相同时的快捷方式。我唯一能想到的是,如果您按 x,y 分布并按 x,y 和 z 排序 我不确定您所说的“您只能在一个字段上存储桶”是什么意思。我认为可以按多个字段进行存储,哈希函数只会获取所有字段并将它们组合起来。【参考方案3】:

我想我回答这个问题迟了,但它不断出现在我的提要中。

Navneet 提供了出色的答案。以视觉方式添加。

分区有助于消除数据,如果在 WHERE 子句中使用,其中分桶有助于将每个分区中的数据组织到多个文件中,因此同一组数据始终写入同一个桶中。对连接列有很大帮助。

假设,您有一个包含五列的表,名称、server_date、some_col3、some_col4 和 some_col5。假设您已在 server_date 上对表进行分区,并将 name 列分为 10 个桶,您的文件结构将如下所示。

    server_date=xyz 00000_0 00001_0 00002_0 ........ 00010_0

这里 server_date=xyz 是分区,000 files 是每个分区中的存储桶。桶是根据一些哈希函数计算的,所以 name=Sandy 的行总是会放在同一个桶中。

【讨论】:

根据上述答案中的 Roberto,server_date 将是一个不好的分区示例,因为它的 cardinality 值非常高。所以你最终会在 hdfs 中拥有太多的文件夹。 server_date 在此作为示例提及。在现实世界中,分区通常按照 Roberto 的描述发生,将日期分解为年/月/日。应该是这样的。【参考方案4】:

Hive 分区:

分区根据表列的值将大量数据分成多个切片。

假设您要存储遍布全球 196 多个国家/地区的人们的信息,涉及约 50 亿条条目。如果您想查询来自特定国家(梵蒂冈城)的人,在没有分区的情况下,您必须扫描所有 50 亿个条目,甚至获取一个国家的数千个条目。如果您根据国家/地区对表进行分区,则可以通过仅检查一个国家/地区分区的数据来微调查询过程。 Hive 分区为列值创建一个单独的目录。

优点:

    水平分布执行负载 在分区数据量少的情况下更快地执行查询。例如从“梵蒂冈城”获取人口返回速度非常快,而不是搜索整个世界的人口。

缺点:

    可能创建的小分区过多 - 目录过多。 对于给定分区的低容量数据有效。但是一些查询,如对大量数据的分组,仍然需要很长时间才能执行。例如与梵蒂冈城的人口分组相比,中国的人口分组需要较长的时间。在数据偏向特定分区值的情况下,分区不能解决响应问题。

Hive 分桶:

分桶将数据分解成更易于管理或相等的部分。

通过分区,您可以根据列值创建多个小分区。如果您进行分桶,您将限制存储数据的桶数。这个数字是在表创建脚本期间定义的。

优点

    由于每个分区中的数据量相等,Map 端的连接会更快。 分区等更快的查询响应

缺点

    您可以在创建表期间定义存储桶的数量,但加载等量数据必须由程序员手动完成。

【讨论】:

【参考方案5】:

在进入Bucketing之前,我们需要了解Partitioning是什么。让我们以下表为例。请注意,我在下面的示例中仅给出了 12 条记录,以供初学者理解。在实时场景中,您可能拥有数百万条记录。

分区 --------------------- Partitioning 用于在查询数据时获取性能。比如上表,如果我们写下面的sql,需要扫描表中的所有记录,降低了性能,增加了开销。

select * from sales_table where product_id='P1'

为了避免全表扫描并仅读取与product_id='P1' 相关的记录,我们可以根据product_id 列将(拆分配置单元表的文件)分区为多个文件。这样,hive 表的文件将被拆分为两个文件,一个为product_id='P1',另一个为product_id='P2'。现在当我们执行上面的查询时,它只会扫描product_id='P1'文件。

../hive/warehouse/sales_table/product_id=P1
../hive/warehouse/sales_table/product_id=P2

创建分区的语法如下。请注意,我们不应将product_id 列定义与以下语法中的非分区列一起使用。这应该只在partitioned by 子句中。

create table sales_table(sales_id int,trans_date date, amount int) 
partitioned by (product_id varchar(10))

缺点:分区时我们应该非常小心。也就是说,它不应该用于重复值数量非常少的列(尤其是主键列),因为它会增加分区文件的数量并增加Name node 的开销。

BUCKETING ----------------- @987654341 @ 用于克服我在分区部分提到的cons。当列中的重复值非常少时(例如 - 主键列),应该使用此选项。这类似于 RDBMS 中主键列索引的概念。在我们的表中,我们可以使用Sales_Id 列进行分桶。当我们需要查询sales_id 列时,它会很有用。

下面是分桶的语法。

create table sales_table(sales_id int,trans_date date, amount int) 
partitioned by (product_id varchar(10)) Clustered by(Sales_Id) into 3 buckets

在这里,我们将进一步将数据拆分为多个分区顶部的文件。

由于我们已指定3 存储桶,因此每个product_id 将其拆分为3 个文件。它在内部使用modulo operator 来确定每个sales_id 应存储在哪个桶中。例如,对于product_id='P1'sales_id=1 将存储在 000001_0 文件中(即 1%3=1),sales_id=2 将存储在 000002_0 文件中> 文件(即 2%3=2),sales_id=3 将存储在 000000_0 文件中(即 3%3=0)等。

【讨论】:

对于数字聚簇列,它总是只取 mod 的桶数吗?对于字符串值的聚集列,它是否使用字符串的 Java hashCode() 作为哈希函数?程序员可以选择散列函数吗? 显然(根据我的实验)hive 使用了 Java 的 hashCode() 方法的变体:github.com/apache/hive/blob/release-1.1.0/serde/src/java/org/…。这是在这里提到的:***.com/questions/30594038/…。【参考方案6】:

区别在于bucketing按Column Name划分文件,partitioning按table内的特定值划分文件

希望我定义正确

【讨论】:

【参考方案7】:

这里有很好的反应。我想简短地记住分区和存储桶之间的区别。

您通常在不太独特的列上进行分区。并在最独特的列上分桶。

如果您以世界人口的国家、人名和他们的生物识别 ID 为例。正如您所猜想的那样,国家字段将是不太独特的列,而生物识别 ID 将是最独特的列。因此,理想情况下,您需要按国家/地区对表进行分区,并按生物识别 ID 对其进行存储。

【讨论】:

【参考方案8】:

强烈建议在 Hive 表中使用分区,原因如下 -

插入 Hive 表应该更快(因为它使用多个线程 将数据写入分区) 从 Hive 表中查询应该是高效且低延迟的。

示例:-

假设输入文件 (100 GB) 已加载到 temp-hive-table 中,其中包含来自不同地区的银行数据。

没有分区的 Hive 表

Insert into Hive table Select * from temp-hive-table

/hive-table-path/part-00000-1  (part size ~ hdfs block size)
/hive-table-path/part-00000-2
....
/hive-table-path/part-00000-n

这种方法的问题是 - 它会扫描整个数据以查找您在此表上运行的任何查询。与使用分区和分桶的其他方法相比,响应时间会很长。

带分区的 Hive 表

Insert into Hive table partition(country) Select * from temp-hive-table

/hive-table-path/country=US/part-00000-1       (file size ~ 10 GB)
/hive-table-path/country=Canada/part-00000-2   (file size ~ 20 GB)
....
/hive-table-path/country=UK/part-00000-n       (file size ~ 5 GB)

优点 - 在查询特定地理交易的数据时,可以更快地访问数据。 缺点 - 通过在每个分区中拆分数据可以进一步改进插入/查询数据。请参阅下面的分桶选项。

带分区和分桶的 Hive 表

注意:使用“CLUSTERED BY(Partiton_Column) 到 5 个存储桶中创建 hive 表 ...”

Insert into Hive table partition(country) Select * from temp-hive-table

/hive-table-path/country=US/part-00000-1       (file size ~ 2 GB)
/hive-table-path/country=US/part-00000-2       (file size ~ 2 GB)
/hive-table-path/country=US/part-00000-3       (file size ~ 2 GB)
/hive-table-path/country=US/part-00000-4       (file size ~ 2 GB)
/hive-table-path/country=US/part-00000-5       (file size ~ 2 GB)

/hive-table-path/country=Canada/part-00000-1   (file size ~ 4 GB)
/hive-table-path/country=Canada/part-00000-2   (file size ~ 4 GB)
/hive-table-path/country=Canada/part-00000-3   (file size ~ 4 GB)
/hive-table-path/country=Canada/part-00000-4   (file size ~ 4 GB)
/hive-table-path/country=Canada/part-00000-5   (file size ~ 4 GB)

....
/hive-table-path/country=UK/part-00000-1       (file size ~ 1 GB)
/hive-table-path/country=UK/part-00000-2       (file size ~ 1 GB)
/hive-table-path/country=UK/part-00000-3       (file size ~ 1 GB)
/hive-table-path/country=UK/part-00000-4       (file size ~ 1 GB)
/hive-table-path/country=UK/part-00000-5       (file size ~ 1 GB)

优点 - 插入速度更快。更快的查询。

缺点 - 分桶会创建更多文件。在某些特定情况下,许多小文件可能会出现问题

希望这会有所帮助!

【讨论】:

以上是关于在 Hive 中对表进行分区和分桶有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

分区和分桶区别

HIVE—索引分区和分桶的区别

深入理解Hive分区与分桶

Hive的分区和分桶

入门大数据---Hive分区表和分桶表

Hive里的分区和分桶再谈