千亿级用户行为日志表,你怎么设计分区和分桶?
先说结论
分区(Partition)是把表按某个字段的值拆成不同的目录,查询时只扫描满足条件的目录。分桶(Bucket)是在分区内部再按某个字段的Hash值分成固定数量的文件,主要用于优化Join和采样。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
这道题直接考察你有没有真正建过生产环境的表。分区和分桶的概念很多人都知道,但“分区字段怎么选”“分桶数怎么定”“什么时候该用分桶”这些实战问题,很多人答不上来。
业务场景:短剧出海公司的用户行为日志表
我当前公司有一张用户行为日志表,每天级5亿条新增数据,字段包括:user_id、event_type、event_time、platform、country、device_id、page_id、session_id等。我是这样设计的:
CREATE TABLE dwd_user_event (
user_id STRING,
event_type STRING,
event_time TIMESTAMP,
platform STRING, -- ios/android/web
country STRING,
device_id STRING,
page_id STRING,
session_id STRING,
-- ... 其他字段
)
PARTITIONED BY (dt STRING, hour STRING)
CLUSTERED BY (user_id) INTO 256 BUCKETS
STORED AS PARQUET;
分区设计的思考过程
为什么用dt + hour双层分区?
单层dt分区每个分区有约5亿条数据,单个分区太大。加上hour后,每个分区约2000万条,更合理。而且很多查询场景是“查某天某几个小时的数据”,双层分区能精确裁剪。
分区字段选择的原则:
① 几乎所有查询都会带的过滤条件(dt几乎每次查询都会用)
② 基数不能太高(country有200+个国家,配合dt就会产生每天200个子分区,一年就是7万+个分区,MetaStore压力巨大)
③ 值分布均匀(如果按platform分区,ios/android/web三个值数据量可能差很多,导致倾斜)
分桶设计的思考过程
为什么按user_id分桶?
因为我们大量的查询场景是“某个用户的行为轨迹”“用户层面的聚合”,按user_id分桶后,同一个用户的数据一定在同一个桶里,做Join和聚合时效率更高。
为什么是256个桶?
经验公式:桶数 ≈ 单个分区的数据量 / 每个桶的理想大小。我们单个小时分区纤2000万条、纤2GB,256个桶每个级8MB,合理。桶数建议是2的幂次,方便Shuffle对齐。
面试高频追问:分区和分桶的区别
| 维度 | 分区(Partition) | 分桶(Bucket) |
|---|---|---|
| 本质 | 按值拆成目录 | 按Hash拆成文件 |
| 目的 | 减少扫描范围 | 优化Join和采样 |
| 数量控制 | 由数据决定(每天一个分区) | 手动指定(256个) |
| 查询优化 | 分区裁剪 | Bucket Map Join |
| 是否必须 | 几乎必须 | 看场景 |
总结
记住一句话:分区减少扫描范围,分桶优化Join效率。面试的时候能结合真实场景说清楚为什么这么选,而不是干巴巴列定义,就很有说服力。
下一篇聊一个很实用的场景题:维度表每天都在变,你怎么处理缓慢变化维?这是数仓建模的核心问题。
你们生产环境的分区策略是什么?评论区聊聊~
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长