Hive分桶机制应用
业务背景:
- 数据提供方的数据频率不固定,很多时候,N天才会推一次,多天的业务数据,会集中到某一个分区中
- 由于分布不均匀,查询的时候,也就需要不固定时间范围的查询。
- 按照业务需求,需要关联维表,回填一些信息。
解决办法:
- 使用动态分区的方式,按业务时间,重新将数据写入新的表。
- 新表设计时候引入分桶策略,方便后期查询。
新表设计:
sql
-- 数据表
create table if not exists new_box(
capture_time bigint comment '采集时间戳',
uid string comment '用户ID',
tags string ,
......
)partitioned by (dt string comment '日期分区')
CLUSTERED BY (uuid) INTO 32 BUCKETS;
-- 用户标签表
create table if not exists user_tags(
uid string ,
tags string
)CLUSTERED BY (uuid) INTO 32 BUCKETS;
动态分区
sql
SET hive.exec.dynamic.partition=true;
SET hive.exec.dynamic.partition.mode=nonstrict;
SET hive.execution.engine=tez;
SET hive.merge.tezfiles=true;
SET hive.merge.size.per.task=268435456; -- 256MB
SET hive.merge.smallfiles.avgsize=16777216; -- 16MB
INSERT INTO TABLE new_box PARTITION (dt)
SELECT
t1.capture_time,
t1.uid,
t2.tags
...
date_format(capture_time, 'yyyy-MM-dd') AS dt
FROM src_data t1
left join
user_tags t2
on t1.uid=t2.uid
DISTRIBUTE BY
hash(uid) % 64,
dt;
如何设计分桶:
分桶设计的4条核心原则:
- 分桶是为了join、去重、抽样,不是为了分区
- 一个表只允许一个分桶键(clustered by 只能是一个字段)
- 分桶键必须是 JOIN/Group By/Distinct 的高频字段
- 分桶数=数据规模 / 单文件理想大小。
分桶设计的标准流程(5步法)
1: 确定分桶键
✅ 优先候选
| 场景 | 分桶键 |
|---|---|
| 事实表 JOIN 维表 | 外键(uuid / user_id) |
| 明细表去重 | 主键 |
| 用户行为分析 | user_id |
| 订单表 | order_id |
❌ 绝对不要
- 时间戳
- 经纬度
- 高基数 + 无意义字段
2:判断是否需要分桶
问自己 3 个问题:
1️⃣ 是否会 频繁 JOIN / 去重 / 抽样?
2️⃣ 数据量是否 ≥ 100GB?
3️⃣ 是否已经有分区?
✅ 满足 2 个以上 → 必须分桶
3:计算bucket数
经验公式: bucket数 ≈ 表数据量 / 单 bucket 理想大小
✅ 推荐单 bucket 大小
| 场景 | 推荐 |
|---|---|
| 离线批处理 | 200--400MB |
| 交互查询 | 100--200MB |
| 日志表 | 256MB |
4.表结构模板
sql
CREATE TABLE xxx (
...
)
PARTITIONED BY (dt STRING)
CLUSTERED BY (bucket_key) INTO 32 BUCKETS
STORED AS PARQUET;
- 分区不等于分桶
- 不要把时间放进分桶
5.写入时预防小文件
sql
INSERT INTO TABLE xxx PARTITION (dt)
SELECT ...
FROM source
DISTRIBUTE BY
hash(bucket_key) % 32,
dt;
备注:
- 控制文件数
- 不影响 bucket 映射
- 与 CLUSTERED BY 逻辑一致
验证是否合理
bucket分布检查:
sql
SELECT hash(uuid) % 32 AS b, count(*)
FROM table
GROUP BY b;
结果:
- 0-31连续
- 行数差距< 20%
JOIN是否命中SMB
sql
explain select ...
# SMB Join Operator
#(利用两张表的分桶信息,直接按 bucket 对齐 JOIN,避免 Shuffle)