浪尖,请问如何确定hive分桶数?

浪尖 浪尖聊大数据

今日,有人在星球问了一个比较好的问题:浪尖,请问如何确定hive的分桶数呢?

关于这个问题,浪尖想写个文章,谈谈我自己的看法,当然也欢迎有经验的同学么留言。

顺便打个广告,更多优质文章和问题答疑及视频教程请点击原文链接,加入浪尖知识星球-Spark技术学院获取。

需要了解hive的分区分桶及二者的区别

hive的分区和分桶

相关hive文章

Hive性能优化(全面)



为啥要分桶?

首先要知道,Hive 分区提供了一种将hive表数据分成多个文件/目录的方法。 但是,它只在少数情况下提供有效的效果,比如:

  • 当分区数量有限时。

-且 分区的大小相对相等。

然而,这基本是不可能。 例如,根据国家/地区等地理位置对表格进行分区。 那么会发现一些较大国家产生的分区会很大(例如:4-5个国家本身占总数据的70-80%)。 小国家会产生小分区(世界上所有国家仍然可能只占总数据的20-30%)。 因此,这时分区将不是理想的。为了解决过度分区的问题,Hive提供了分桶的概念。 这是将表数据集分解为更易于管理的部分的另一种有效技术。



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并不能确保正确填充表。 数据加载到存储桶需要由我们自己处理。



举个例子

创建分区分桶表

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


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命令加载数据到分桶表,而是需要使用INSERT OVERWRITE TABLE ... SELECT ... FROM子句来填充分桶表。 为此,我们将在hive中创建一个临时表,其中包含该表中输入文件中的所有列,我们将复制到目标bucketed表中。

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

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


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语句如下


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,分桶的列基数要大,也即是该列去重后的值要大。

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

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