常见分区策略:范围、列表、哈希、复合
分区策略这东西,说白了就是决定「数据该去哪台机器」。
我见过不少团队,上来就拍脑袋选哈希分区,结果后面扩容时哭都哭不出来。选分区策略,得先搞清楚你的数据长什么样、查询怎么走。
下面这四种,是我在实际项目中用得最多的。咱们一个一个拆开看。
1. 范围分区(Range Partitioning)
按某个字段的连续区间来分。比如按时间、按ID范围。
核心逻辑: 数据按值的大小,落入不同的区间段。
-- 示例:按用户ID范围分区
CREATE TABLE users (
id BIGINT,
name VARCHAR(100),
created_at TIMESTAMP
) PARTITION BY RANGE (id) (
PARTITION p0 VALUES LESS THAN (1000000),
PARTITION p1 VALUES LESS THAN (2000000),
PARTITION p2 VALUES LESS THAN (3000000),
PARTITION p3 VALUES LESS THAN MAXVALUE
);
适用场景:
- 时间序列数据(日志、订单、监控指标)
- 需要做范围扫描的查询(比如查某个月的数据)
- 数据有明显冷热分层(旧数据归档)
优点:
- 范围查询效率极高,直接裁剪掉无关分区
- 分区管理直观,加分区、删分区都很方便
- 数据局部性好,相邻数据大概率在同一分区
缺点:
- 容易产生数据倾斜——热点数据全挤在一个分区
- 跨分区查询需要合并结果,性能下降
我的经验: 做时间范围分区时,我习惯把分区粒度设成「天」或「小时」。太粗了热点集中,太细了分区数爆炸。曾经有个日志系统,我按分钟分区,结果一天产生1440个分区,元数据管理直接崩了。
2. 列表分区(List Partitioning)
按离散的枚举值来分。比如按地区、按状态、按业务线。
-- 示例:按地区分区
CREATE TABLE orders (
order_id BIGINT,
region VARCHAR(20),
amount DECIMAL(10,2)
) PARTITION BY LIST (region) (
PARTITION p_north VALUES IN ('北京','天津','河北'),
PARTITION p_south VALUES IN ('广东','广西','海南'),
PARTITION p_east VALUES IN ('上海','江苏','浙江'),
PARTITION p_other VALUES IN ('其他')
);
适用场景:
- 数据有明确的分类属性
- 查询经常按某个枚举字段过滤
- 多租户场景,按租户ID分区
优点:
- 语义清晰,一眼就能看出数据归属
- 管理方便,增删分区不影响其他分区
- 适合做数据隔离(比如不同租户物理隔离)
缺点:
- 枚举值分布不均时,照样倾斜
- 新增枚举值需要手动加分区
避坑指南: 我曾经在一个电商项目里,用「省份」做列表分区。结果双十一期间,广东、浙江几个大省的单量是其他省的几十倍。那几个分区直接被打满,其他分区却闲着。后来我改成按「省份+用户ID哈希」的复合分区,才把负载摊平。
3. 哈希分区(Hash Partitioning)
对分区键做哈希计算,然后模上分区数,决定数据去哪。
-- 示例:按用户ID哈希分区(4个分区)
CREATE TABLE users (
id BIGINT,
name VARCHAR(100)
) PARTITION BY HASH (id) PARTITIONS 4;
-- 数据分布逻辑(伪代码)
partition_id = hash(user_id) % 4
适用场景:
- 数据分布需要尽量均匀
- 没有明显的范围查询需求
- 写操作频繁,需要分散写入压力
优点:
- 数据分布最均匀,基本不会倾斜
- 写入吞吐高,各分区压力均衡
- 适合做负载均衡的基础策略
缺点:
- 范围查询基本废了,得扫全部分区
- 扩容时数据需要大量迁移(分区数变了,哈希结果全变)
- 分区数一旦确定,改起来很痛苦
我的建议: 哈希分区数最好设成2的幂次(比如8、16、32)。这样扩容时可以用「一致性哈希」或「虚拟桶」来减少迁移量。我有个项目一开始设了7个分区,后来加节点时,数据迁移量接近100%,差点没把运维累死。
4. 复合分区(Composite Partitioning)
把上面几种策略组合起来用。先做一层分区,再在内部做第二层分区。
-- 示例:先按时间范围分区,再按用户ID哈希子分区
CREATE TABLE orders (
order_id BIGINT,
user_id BIGINT,
created_at TIMESTAMP
) PARTITION BY RANGE (YEAR(created_at))
SUBPARTITION BY HASH (user_id) SUBPARTITIONS 4 (
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026)
);
适用场景:
- 既要范围裁剪,又要写入均衡
- 数据量大到单层分区不够用
- 需要同时满足多种查询模式
常见组合:
| 组合方式 | 第一层 | 第二层 | 典型场景 |
|---|---|---|---|
| 范围-哈希 | 时间范围 | 用户ID哈希 | 订单系统,按时间归档+写入均衡 |
| 范围-列表 | 时间范围 | 地区列表 | 多区域日志,按时间+区域查询 |
| 列表-哈希 | 租户列表 | 业务ID哈希 | 多租户SaaS,租户隔离+内部均衡 |
| 哈希-范围 | 用户ID哈希 | 时间范围 | 用户维度的时序数据 |
优点:
- 灵活性最高,能应对复杂场景
- 兼顾范围查询和写入均衡
- 数据管理粒度更细
缺点:
- 元数据复杂,管理成本高
- 查询优化器不一定能充分利用两层裁剪
- 运维操作(加分区、合并分区)更繁琐
核心原则: 复合分区不是越多层越好。两层基本够用,三层以上我建议你重新审视设计。每多一层,查询计划和元数据管理的复杂度就翻一倍。
四种策略对比一览
| 策略 | 数据分布 | 范围查询 | 写入均衡 | 扩容难度 | 典型场景 |
|---|---|---|---|---|---|
| 范围分区 | 可能倾斜 | 优秀 | 差 | 低 | 时间序列、日志 |
| 列表分区 | 可能倾斜 | 中等 | 中等 | 低 | 多租户、分类数据 |
| 哈希分区 | 均匀 | 差 | 优秀 | 高 | 高并发写入、缓存 |
| 复合分区 | 可控 | 良好 | 良好 | 中 | 复杂业务、海量数据 |
分区策略选择决策流程
下面这张图,是我自己总结的选型思路。每次做分区设计,我都会过一遍这个流程。
嗯,这个流程我用了好几年。每次做分区设计,先问自己三个问题:
- 查询模式是什么? 范围查询多,还是点查多?
- 写入压力在哪? 有没有热点?要不要均衡?
- 未来怎么扩容? 分区数能变吗?迁移成本能接受吗?
我的血泪教训: 曾经有个金融项目,一开始只用了哈希分区。后来业务方要求按时间范围查历史订单,每次都得扫全部分区,慢得要命。最后不得不改成复合分区,数据迁移搞了整整一个周末。所以,分区策略一定要提前想清楚查询模式,不然后面改起来成本极高。
四种策略没有的好坏。关键看你的数据特征和业务需求。我个人习惯是:
- 时序数据 → 范围分区
- 多租户 → 列表分区
- 高并发写入 → 哈希分区
- 复杂场景 → 复合分区
记住一点:分区是手段,不是目的。别为了分区而分区,搞得系统复杂得没法维护。