在Hive中对表进行分区和存储有什么区别?


129

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

Answers:


247

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

... /员工/ 国家 / 地区= ABC / DEPT = XYZ

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

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


4
谢谢Navneet。但是,您能否详细说明分区如何进行存储?假设如果我们在CLUSED BY子句中指定了32个存储桶,并且CREATE TABLE语句还包含Partitioning子句,那么如何将分区和存储桶一起管理?分区数是否限制为32?或为每个分区创建32个存储桶?每个存储区都是HDFS文件吗?
sgsi 2015年

12
配置单元表可以同时具有分区和存储区。根据您的partition子句,每个分区将创建32个存储桶。是HDFS文件。
Navneet Kumar

7
@sgsi分区是一个文件夹,存储桶是一个文件。
leftjoin '16

12
根据记录,此答案来自Programming Hive的文本(O'Reilly,2012年)。
ianmcook

1
我发现此链接很有用。它具有可以为该答案增加更多价值的信息。linkedin.com/pulse/...
亚历克斯·拉吉Kaliamoorthy

129

前面的解释中缺少一些细节。为了更好地了解分区和存储的工作原理,您应该查看数据在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

然后配置单元会将数据存储在目录层次结构中,例如

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

因此,在分区时必须格外小心,因为例如,如果按employee_id进行分区,并且拥有数百万个员工,那么文件系统中最终将拥有数百万个目录。术语“ 基数 ”是指字段可以具有的可能值的数量。例如,如果您有一个“国家”字段,则世界上的国家大约为300个,因此基数约为300。对于“ timestamp_ms”这样的字段(每毫秒更改一次),基数可以达到数十亿。通常,在选择要分区的字段时,它的基数不应该很高,因为文件系统中的目录太多了。

另一方面,由于也指定了存储桶的数量,因此集群化也称为存储桶将具有固定数量的文件。配置单元将执行的操作是获取字段,计算哈希值并将记录分配给该存储桶。但是,如果您使用256个存储桶,而您存储的字段的基数较低(例如,这是美国州,则只能有50个不同的值),会发生什么?您将拥有50个带数据的存储桶,以及206个无数据的存储桶。

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

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

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


您可以在一个示例中用SORTED-BY解释CLUSTERED-BY行为吗?根据我的示例,我发现SORTED-BY无所事事。我错过了什么吗?
Jagadish Talluri

2
x,y的CLUSTERED BY就像编写x,y的DISTRIBUTE BY x,y(请参阅cwiki.apache.org/confluence/display/Hive/…)一样,因此将SORT BY添加到CLUSTERED BY无效。
罗伯托·贡纽

有趣的是,我同意选择查询中的用法。但是想知道为什么人们在表创建语句中使用聚类和排序。如果在DDL中对SORTED BY没有意义,那么为什么存在此关键字?没有得到。
Jagadish Talluri

SORTED BY旨在与DISTRIBUTED BY一起使用。例如,您可能想按用户ID进行分配,并按时间段在存储桶中进行排序。当SORTED BY和DISTRIBUTED BY上的子句相同时,CLUSTER BY只是一个快捷方式。我唯一能想到的就是,您是按x,y进行分布,还是按x,y和z进行排序
Roberto Congiu

我不确定“只能在一个字段上进行存储”是什么意思。我认为有可能按多个字段进行存储,散列函数将仅采用所有字段并将其组合。
Istvan

18

我想我回答这个问题很晚,但是它一直在我的提要中出现。

Navneet提供了很好的答案。视觉上添加。

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

假设您有一个包含五列的表,它们分别是名称,server_date,some_col3,some_col4和some_col5。假设您已经在server_date上对表进行了分区,并在10个存储桶中的name列上进行了存储,您的文件结构将如下所示。

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

这里server_date = xyz是分区,000个文件是每个分区中的存储桶。存储桶是基于某些哈希函数计算的,因此name = Sandy的行将始终位于同一存储桶中。


2
根据Roberto的说法,server_date将是一个不好的例子,因为它的基数确实很高。因此,您最终将在hdfs中拥有太多文件夹。
朗沙阿

这里以server_date为例。在现实世界中,通常按照罗伯托的描述进行分区,方法是将日期分为年/月/日。那是应该的。
Priyesh

17

配置单元分区:

分区基于表列的值将大量数据分为多个切片。

假设您要存储遍布196个以上国家/地区的人的信息,这些人的条目总数约为50亿。如果要查询来自特定国家(梵蒂冈)的人员,在没有分区的情况下,您必须扫描所有50亿条目,甚至要获取一个国家的数千条目。如果按国家对表进行分区,则只需检查一个国家分区的数据就可以优化查询过程。配置单元分区为列值创建一个单独的目录。

优点:

  1. 水平分配执行负载
  2. 如果分区的数据量较少,则查询执行速度更快。例如,从“ 梵蒂冈城 ” 获得人口很快就会返回,而不是搜索整个世界的人口。

缺点:

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

蜂箱存储:

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

使用分区时,可能会基于列值创建多个小分区。如果要进行存储分区,则将限制存储数据的存储分区的数量。此编号是在表创建脚本期间定义的。

优点

  1. 由于每个分区中的数据量相等,因此Map端的联接会更快。
  2. 更快的查询响应,例如分区

缺点

  1. 您可以在创建表的过程中定义存储桶的数量,但是必须由程序员手动加载相等数量的数据。

9

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

在此处输入图片说明



分区
---------------------
Partitioning用于在查询数据时获得性能。例如,在上表中,如果我们编写以下sql,则需要扫描表中的所有记录,这会降低性能并增加开销。

select * from sales_table where product_id='P1'

为了避免全表扫描并仅读取与之相关的记录,product_id='P1'我们可以基于product_id列将分区(拆分配置单元表的文件)划分为多个文件。这样,配置单元表的文件将被拆分为两个文件,一个为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
------------------
Bucketing用于克服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存储桶,因此每个存储桶都分为3个文件product_id。它在内部用于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?对于字符串值的聚集列,它是否使用hashCode()字符串的Java 作为哈希函数?程序员可以选择哈希函数吗?
唐·史密斯,

显然(根据我的实验),hive在Java的hashCode()方法上使用了一种变体:github.com/apache/hive/blob/release-1.1.0/serde/src/java/org/…。这里提到了这一点:stackoverflow.com/questions/30594038/…
唐·史密斯,

3

所不同的是瓢泼大雨按列名鸿沟的文件,分区下划分的文件使用一个特定的值内表

希望我定义正确


0

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

通常,您会在一个唯一性较低的列上进行分区。并在最独特的列中进行分类。

例如,如果您以国家,国家/地区名称和生物特征ID为代表的世界人口作为示例。您可能会猜到,“国家/地区”字段将是唯一性较差的列,而生物识别ID将是唯一性最高的列。因此,理想情况下,您需要按国家/地区对表格进行分区,并按生物特征ID对其进行分类。


-1

出于以下原因,强烈建议在Hive表中使用分区:

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

例子:-

假设输入文件(100 GB)已加载到temp-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

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

带有分区的配置单元表

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个存储桶...

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)

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

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

希望这会有所帮助!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.