hive分桶表使用

分区和分桶

分区:

  • 分区是表的部分列的集合
  • 在许多场景下,可以通过分区的方法减少每一次扫描总数据量
  • 每一个子目录包含了分区对应的列名和每一列的值,分区列不存储在数据文件中

分桶:

  • 通过对指定列进行哈希计算来实现的,通过哈希值将一个列名下的数据切分为一组桶,并使每个桶对应于该列名下的一个存储文件
  • hive使用对分桶所用的值进行hash,并用hash结果除以桶的个数做取余运算的方式来分桶,保证了每个桶中都有数据,而且均匀分布

虽然分桶是hive的建表机制,但是,从实际测试结果来看,分桶确实对impala的JOIN查询性能有所提升,怀疑是一下原因:

  • 增加文件数,增加并发度
  • 根据uid数据均匀分布多个桶,很大程度上增加了本地命中率
  • 桶内排序+impala统计信息,min/max存储索引加速过滤

更新
impala是能识别hive的分桶信息的:

创建分桶表

以下SQL,创建一个按pt_dt分区,按uid分10个桶的表,并且每个桶内按uid排序

1
2
3
4
5
6
7
CREATE TABLE t (
uid STRING COMMENT '用户uid',
)
PARTITIONED BY (pt_dt STRING)
CLUSTERED BY (uid) SORTED BY (uid ASC) INTO 10 BUCKETS
ROW FORMAT delimited FIELDS TERMINATED BY ','
STORED AS PARQUET TBLPROPERTIES('parquet.compression'='SNAPPY');

插入数据

注意:插入数据必须设置以下参数,不然会造成分桶数据不均

1
SET hive.enforce.bucketing = true;

以下参数也需要配合设置

1
2
3
4
5
6
7
8
# 开启动态分区相关配置
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
# 根据分区数和数据量适当调整
SET hive.exec.max.dynamic.partitions=2048;
SET hive.exec.max.dynamic.partitions.pernode=512;
SET mapreduce.map.memory.mb=8000;
SET mapred.reduce.tasks=100;

插入数据

1
2
INSERT OVERWRITE TABLE t PARTITION(pt_dt) 
SELECT * from t2;

问题

如果在同一分区,不同批次INSERT INTO的数据,分桶文件不会追加,而是重新生成大量文件

比如,向同一分区分两次插入数据,分桶文件会*2

所以尽量使用INSERT OVERWRITE,避免INSERT INTO操作