考察点
分区和分桶是 Hive 建模的两个基础手段,面试官想看你是否理解它们在存储层面的真实形态,以及什么时候该用哪个。常见追问:动态分区踩过什么坑、分桶为什么能加速 join、分区粒度怎么定。
参考答案
分区:按列值切目录
分区表建表时用 PARTITIONED BY (ds string) 声明分区列,物理上每个分区是表目录下的一个子目录,比如 /warehouse/ods.db/user_log/ds=20260701/。分区列不占用数据文件的列空间,是目录名里带的。
分区的核心价值是分区裁剪:查询带上 where ds='20260701',引擎只扫这一个目录,跳过其他分区。一张全量快照表每天 500GB,带分区裁剪查一天和全表扫的性能差是数量级的。
设计分区要把握两点:
- 分区粒度由查询模式决定。日志表按天(ds)分区是行业惯例;数据量特别大再按小时加二级分区(hour);数据量小但查询总是按月汇总的,按月分区反而更省小文件。分区数控制在万级以内——每个分区都是 Metastore 里的一条记录,分区上百万后 Metastore 的 MySQL 会成为瓶颈,建表
describe都变慢。 - 分区列的基数要低。拿 user_id、device_id 这种高基数字段当分区列是典型反模式,每天产生几万个分区,Metastore 直接撑不住。
动态分区的坑
insert overwrite table t partition(ds) select ..., ds from src 是动态分区写法,分区值由 select 最后一列决定。生产上必须配三个参数:
set hive.exec.dynamic.partition.mode=nonstrict; -- 允许全动态分区,strict 模式要求至少一个静态分区
set hive.exec.max.dynamic.partitions=1000; -- 单个 mapreduce 最多创建的分区数
set hive.exec.max.dynamic.partitions.pernode=100;-- 每个 map/reduce 节点最多创建的分区数
坑有两个:一是 ds 字段里混进脏数据(比如日期格式错乱的 2026-0701),会瞬间创建一堆垃圾分区,我处理过一次任务上游数据带 null,一夜之间建出几十万个 ds=__HIVE_DEFAULT_PARTITION__ 之外的异常分区,Metastore 直接报警;二是每个动态分区对应一批输出文件,分区多时小文件爆炸。所以动态分区任务后面通常跟一个文件合并步骤。
分桶:按哈希散列文件
分桶表建表时 CLUSTERED BY (user_id) INTO 32 BUCKETS,物理上每个分区内按 user_id 的哈希值把数据散到 32 个文件(实际写入时桶数受 reduce 个数控制)。分桶不改变目录结构,改变的是文件内数据的分布。
分桶的使用场景比分区窄得多,主要三个:
- 数据抽样:
TABLESAMPLE(BUCKET x OUT OF 32)直接读一个桶,比全表随机采样快得多,调试用。 - 分桶 join / SMB join:两张大表都按 join key 分桶且桶数成倍数关系,可以走 Sort-Merge Bucket join,map 端直接对对应桶做归并,不用全量 shuffle。这是分桶最大的价值,但要求两表都严格分桶且数据分布均匀,实际生产维护成本高,更多是被 Spark 的 sort merge join 和 broadcast join 替代。
- 配合分桶索引:老版本 Hive 的特性,现在基本没人用了。
分区 vs 分桶对照
| 维度 | 分区 | 分桶 |
|---|---|---|
| 物理形态 | 子目录 | 目录内散列文件 |
| 目的 | 查询裁剪 | 控制数据分布、抽样、SMB join |
| 分区/桶数 | 由数据决定,可新增 | 建表时固定 |
| 列基数要求 | 低基数 | 无严格要求,但分布要均匀 |
| 高频使用 | 天天用 | 特定场景用 |
一句话总结:分区是给查询用的,分桶是给 join 和采样用的;建模先把分区设计对,分桶不是默认选项。
可能的追问
- 分区表插入数据时不写分区字段会怎样? 静态分区表会报错(strict 模式下必须显式指定);动态分区会走默认分区
__HIVE_DEFAULT_PARTITION__,数据看似进去了其实进了垃圾分区,查不出来。 - 一张表既有分区又有分桶怎么理解? 先按分区切目录,每个分区目录内再按桶列哈希散文件,两者是正交的。分桶一般只在明确要 SMB join 时才加。
- 分桶数怎么定? 按单桶文件大小估算,让每个桶接近一个 block(128M/256M)的整数倍,同时是 2 的幂方便和下游表对齐。桶数定完改不了,改桶数要重建表。