分区字段选错了会怎样?——一次错误改写要动几百 TB
本文目录
先说结论
分区的目的只有两个:(1)让查询能裁剪(PartitionFilter 只扫相关分区);(2)让写入能并行(不同分区写不同目录)。两者兼顾选字段。
90% 场景按日期分区是对的,但要分清『业务日期』和『处理日期』——跨时区、补数、延迟到达这几个场景两者不同。
分区粒度要匹配查询粒度。大多数查询按天查就按天分区、按小时查就按小时分区。过细(按分钟)分区数炸、过粗(按月)裁剪无效。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
分区设计是数仓建模题的最后一道常问题。题目看起来简单——『一张 10 亿行的用户订单表,按什么分区?』——很多人不假思索答『按日期』。但真实项目里,分区选错的代价极其大:写入热点、查询性能不均、改回来要重刷几百 TB 数据。这题考的是你有没有因为分区选错而痛过。
业务场景:某出海硬件品牌 10 亿行订单表的分区选择
我在某出海硬件品牌有张订单事实表日均 800 万行、累计 10 亿+。分区选型当时有三个候选:(1)按 order_date 分区;(2)按 dt(处理日期)分区;(3)按 region + dt 二级分区。最后选了第三个,原因下面详细讲。
-- 候选方案一:按 order_date
CREATE TABLE fact_order_v1 (...)
PARTITIONED BY (order_date DATE);
-- 问题:跨时区订单落到哪个分区?
-- 美国用户 3/1 下单(美东时间),按 UTC 是 3/2
-- 补数修正历史数据要重刷分区
-- 候选方案二:按 dt(处理日期)
CREATE TABLE fact_order_v2 (...)
PARTITIONED BY (dt STRING);
-- 问题:所有订单都落到『今天』分区,历史数据重新灌需要改分区
-- 最终方案:按 region + dt
CREATE TABLE fact_order (
order_id BIGINT, uid BIGINT, amount DECIMAL, ...
) PARTITIONED BY (region STRING, dt STRING);
-- region=US 的数据都落 US 目录,dt 按用户本地日
-- 优点:裁剪按地区 + 按日期,绝大多数分析都能裁
段落一:分区的本质——目录隔离
分区在 Hive/Spark/Iceberg 里本质都是目录或 manifest 隔离。分区字段 = 目录名。查询时的 WHERE 过滤能推到分区层、直接跳过不相关目录——这就是『分区裁剪』。
Plain
按 dt 分区的物理存储:
/warehouse/fact_order/
dt=2024-03-13/
part-00001.parquet
part-00002.parquet
...
dt=2024-03-14/
part-00001.parquet
...
dt=2024-03-15/ ← WHERE dt='2024-03-15' 只读这一个目录
...
如果 WHERE 里没写 dt,就是全表扫 = 扫完所有目录。
所以分区表没写分区过滤 = 没分区。
段落二:选分区字段的 4 条标准
| 标准 | 理由 | 反例 |
|---|---|---|
| 被 WHERE 频繁用到 | 不会被 WHERE 用的字段分区毫无意义 | 按 user_id 分区(查询几乎不会 WHERE user_id 过滤) |
| 基数适中(100-10000) | 太低裁不掉,太高分区爆炸 | 按 status(基数 < 10)或按 uid(基数 > 千万) |
| 分布相对均匀 | 倾斜分区照样慢 | 按 country(中国数据占 80%) |
| 业务稳定不变 | 分区字段变值 = 整行要挪位 | 按用户当前 VIP 等级分区 |
段落三:按『业务时间』vs『处理时间』的经典选择
大部分日志/事件表都有两个时间戳:event_time(事件发生时间)和 process_time(ETL 处理时间)。按哪个分区?
按 process_time(dt)的优势:写入只改最新分区、ETL 幂等;劣势:跨时区时同一用户的同一天数据会被拆到两个分区。
按 event_time 的优势:业务查询最自然(按事件发生日查);劣势:数据迟到会回写历史分区,ETL 幂等难做。
我的实战经验:小厂用 dt(简单稳定),大厂出海场景按业务时间(跨时区必须)。我在某短剧出海公司就是按 『用户本地 event_date』分区的——相当于按用户时区切日。这个设计是踩了 dt 分区的坑之后改的。
段落四:二级分区——什么时候要、什么时候不要
二级分区(比如 region + dt)能进一步减小扫描范围,但不是所有场景都要用。
Plain
二级分区适合的场景:
✅ 查询经常按两个字段联合过滤
✅ 第一个字段基数 10-100(比如地区、业务线)
✅ 数据量大(> 10 亿行),裁剪价值显著
二级分区不适合的场景:
❌ 第一级字段基数 > 1000,分区数爆炸
❌ 查询只按一个字段过滤,另一个没用
❌ 小表(< 1 亿行),裁剪价值小于管理成本
分区数上限(经验值):
Hive:单表 10 万分区以内
Spark / Delta:同上
超过就要考虑桶表(bucket)或调整分区粒度
段落五:分区粒度——按天还是按小时
实时数仓和 IoT 场景常见按小时分区。选按天还是按小时的标准:
| 粒度 | 适合场景 | 风险 |
|---|---|---|
| 按月 | 冷历史表、合规归档 | 分区太粗、裁剪弱 |
| 按天 | 绝大多数离线数仓 | 最平衡的默认选项 |
| 按小时 | 实时看板、IoT 高频事件 | 分区数量膨胀,24×天数 |
| 按分钟 | 超高频、短保留期 | 分区数极大,管理成本高 |
经验值:一张表的分区总数控制在 1-10 万,超过 10 万要考虑合并。按小时分区留一年就是 8760 个分区,已经不小了。
面试高频追问
追问一:如果当初选错了分区字段,怎么改?
这是最痛的问题。改分区字段 = 重建表 + 重写所有数据 + 下游 SQL 全改。具体步骤:(1)新建一张带正确分区字段的表;(2)从原表 INSERT OVERWRITE 到新表(按新分区字段重新组织数据);(3)下游 SQL 批量改过来;(4)切流量 / 灰度;(5)下线老表。我在某出海硬件品牌改过一次,改的是从 dt 改成 region+dt,10 亿行数据重刷了 8 小时,下游 40 个任务逐个改。教训是:分区字段第一次就要选对,返工成本太大。
追问二:分区字段和桶字段的区别是什么?
分区是目录隔离、主要服务查询裁剪;桶是文件内哈希散列、主要服务 Join 和采样。一般分区字段选『查询 WHERE 常用』(比如 dt),桶字段选『Join Key』(比如 uid)。桶表 Join 能避免 Shuffle——两边相同 uid 进同样的桶,Map-side Join 直接做。但实务里维护成本高,大多数项目只用分区,桶表用得不多。
追问三:Iceberg / Hudi 这种数据湖格式的分区和 Hive 有什么不一样?
数据湖格式引入了『隐藏分区』和『分区演化』两个大特性。隐藏分区:你只 WHERE event_time,Iceberg 自动根据分区规范 event_time.day 做裁剪,不用 SQL 里写 dt=。分区演化:可以在线修改分区规则(比如从 day 改成 hour),旧数据不用重写,新数据按新规则。这个能力正好解决了上一题的痛点——改分区字段不用全表重刷了。
实战踩坑:按 status 分区的翻车故事
我见过一个同事设计过『按 order_status 分区』的订单表。初衷是『status 是 WHERE 常用字段』。但 order_status 只有 5 种值(待付款/已付款/已发货/已完成/已取消),基数太低,分区裁剪基本没意义;更糟的是 status 会变——订单从『待付款』变『已付款』,对应记录要从一个分区挪到另一个分区,HDFS 里的分区表做不到『update 字段』,只能重写。
这个表上线一周就翻车了,改回按 dt 分区重写了全表。教训:分区字段的『值稳定』比『查询常用』更重要。变值的字段坚决不能做分区字段。
Plain
分区字段选型红线(我的硬规则):
1. 字段值会变 → ❌ 禁止(order_status / vip_level)
2. 字段基数 < 10 → ❌ 禁止(gender / is_active)
3. 字段基数 > 100万 → ❌ 禁止(uid / order_id)
4. 字段分布极度倾斜 → ⚠️ 警告(country 且 70%+ 一个国家)
5. 字段不会被 WHERE 用 → ❌ 禁止(created_by / source_ip)
总结
分区题的高分答案是能讲出三件事:(1)选分区字段的 4 条标准;(2)业务时间 vs 处理时间的选择(尤其出海场景);(3)一个真实的返工故事。分区看起来简单,实则是建模里最容易犯方向性错误的地方——选错了改回来的成本远超第一次多想半天。
下一篇:『新表上线数据错了怎么查?』——进入第四阶段『生产问题』。面试的难度曲线到这里开始上升,考察的是真实故障处置能力。
你们表的分区字段有没有出现过『选错了但懒得改』的遗产?还是已经经历过血的重刷?
关注「Wood聊大数据」| 9年大数据老兵,聊技术、聊职场、聊成长
数仓开发 面试通关手册
第11-15篇:生产问题处置 + 架构起点(面试 35-50 分钟)
Wood聊大数据
9年大数据老兵,聊技术、聊职场、聊成长
按面试官真实提问顺序设计的 20 道题
第三部分导读(生产问题处置 + 架构起点)
面试进行到 35 分钟之后,话题会从『你会做什么』拐到『你扛过什么』。这本册的第 11-14 篇就是典型的生产问题处置四连问——新表数据错了怎么查、T+1 变 T+2 怎么办、上游 schema 悄悄变了怎么防、1200 行 SQL 怎么 review。这四题能考出一个候选人是不是真的在线上待过。
第 15 篇是架构起点——同指标多部门不一致。这题既有生产问题的痕迹(故障驱动),又有架构视野(需要跨团队、跨系统的解决方案)。答好这题相当于给自己从『执行型』往『架构型』转身一个台阶。
这五题里几乎每一题都有我踩过的具体故事:汇率含义变导致礼物总额暴涨 300%、跨时区切日让美国数据延迟一天、1200 行 SQL 重构到 80 行、CEO 问『DAU 到底信哪个』。面试时能讲出具体故事就能压住节奏。
— Wood,2026 年春