分区类型详解:范围分区、列表分区、哈希分区、复合分区
分区表的核心,说白了就是「分而治之」。把一张大表拆成多个物理小段,查询时只扫相关分区,性能自然就上来了。但分区类型怎么选?这问题我几乎每次做架构评审都会被问到。
今天咱们就把四种主流分区类型掰开揉碎讲清楚。我会结合自己踩过的坑,帮你建立选型直觉。
范围分区(Range Partitioning)
范围分区是最直观、最常用的分区方式。按某个列的连续区间划分数据,比如按时间、按ID范围。
CREATE TABLE orders (
order_id BIGINT,
order_date DATE,
amount DECIMAL(10,2)
)
PARTITION BY RANGE (order_date) (
PARTITION p_2024q1 VALUES LESS THAN ('2024-04-01'),
PARTITION p_2024q2 VALUES LESS THAN ('2024-07-01'),
PARTITION p_2024q3 VALUES LESS THAN ('2024-10-01'),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
我个人习惯用范围分区做数据生命周期管理。比如订单表,三个月前的数据直接分区裁剪掉,查询效率提升明显。但要注意——边界值设计很关键。我曾经见过有人把分区边界设成 '2024-12-31',结果12月31日当天的数据全进了下一个分区,排查了半天。
列表分区(List Partitioning)
列表分区是按离散值列表来划分。比如按地区、按业务类型、按状态码。数据值有限且固定时,列表分区特别好用。
CREATE TABLE user_profiles (
user_id BIGINT,
region VARCHAR(20),
created_at TIMESTAMP
)
PARTITION BY LIST (region) (
PARTITION p_north VALUES IN ('北京', '天津', '河北'),
PARTITION p_south VALUES IN ('广东', '广西', '海南'),
PARTITION p_east VALUES IN ('上海', '江苏', '浙江'),
PARTITION p_other VALUES IN (DEFAULT)
);
嗯,这里要注意——列表分区不支持多列组合,只能基于单列。而且如果业务枚举值会频繁变化,列表分区维护起来挺头疼的。我有个项目,业务方每季度新增一个地区编码,每次都要 ALTER TABLE ADD PARTITION,运维成本不低。
哈希分区(Hash Partitioning)
哈希分区不关心数据本身的业务含义,而是对分区键做哈希运算,均匀分布到N个分区。适合数据分布不均匀、没有明显范围或列表特征的场景。
CREATE TABLE session_logs (
session_id VARCHAR(64),
event_time TIMESTAMP,
payload TEXT
)
PARTITION BY HASH (session_id)
PARTITIONS 8;
哈希分区的核心优势是数据均匀分布。但代价是——你没法做分区裁剪。查询时必须扫描所有分区,除非 where 条件里精确匹配了分区键。我记得有一次做性能优化,发现哈希分区表上跑范围查询,反而比普通表还慢,就是因为每个分区都要扫一遍。
复合分区(Composite Partitioning)
复合分区就是「分区套子分区」。先按一种策略做一级分区,再在每个分区内按另一种策略做二级分区。说白了,把两种分区方式的优点结合起来。
CREATE TABLE metrics (
metric_name VARCHAR(50),
ts TIMESTAMP,
value DOUBLE
)
PARTITION BY RANGE (ts)
SUBPARTITION BY HASH (metric_name)
SUBPARTITIONS 4 (
PARTITION p_2024q1 VALUES LESS THAN ('2024-04-01'),
PARTITION p_2024q2 VALUES LESS THAN ('2024-07-01'),
PARTITION p_2024q3 VALUES LESS THAN ('2024-10-01'),
PARTITION p_2024q4 VALUES LESS THAN ('2025-01-01')
);
这个例子很典型:按时间范围做一级分区,方便数据归档;再按指标名称做哈希子分区,保证同一时间范围内数据均匀分布。我在监控系统里用过这种方案,查询某段时间内所有指标时,先裁剪到对应时间分区,再并行扫描子分区,性能提升非常明显。
各类型对比与选型
下面这张表是我自己整理的选型对照表,每次做架构设计都会拿出来参考。
| 分区类型 | 数据分布 | 分区裁剪 | 维护成本 | 典型场景 |
|---|---|---|---|---|
| 范围分区 | 按连续区间 | 优秀(范围查询) | 低(定期加分区即可) | 日志、订单、时序数据 |
| 列表分区 | 按离散值 | 优秀(等值查询) | 中(枚举变化需维护) | 地区、状态、分类 |
| 哈希分区 | 均匀分布 | 差(仅等值匹配) | 高(扩分区需重建) | 会话、流水、随机ID |
| 复合分区 | 分层分布 | 优秀(一级裁剪) | 高(两层管理) | 大规模时序+多维查询 |
选型决策流程
我一般按下面这个流程来选型,你可以参考一下。
- 先看查询模式——如果查询经常带范围条件,优先范围分区;如果是等值查询,列表或哈希都可以。
- 再看数据分布——数据有明显的时间或序列特征?用范围。数据离散且均匀?用哈希。数据有固定分类?用列表。
- 考虑维护成本——团队运维能力有限?别碰复合分区。数据量极大且需要归档?范围分区最省心。
- 最后看数据库能力——有些数据库不支持复合分区,有些对分区数有限制。先查文档再动手。
分区类型关系图
下面这张图展示了四种分区类型的关系和选型路径,帮你快速建立整体认知。
选型避坑总结
最后,我把自己这些年踩过的坑总结成几条原则,你记一下。
- 别过度设计——能用范围分区解决的问题,别上复合分区。多一层分区多一层维护成本。
- 分区键要稳定——分区键一旦选定,后期修改代价极大。我曾经见过有人用「创建时间」做分区键,结果业务改了数据写入逻辑,时间字段经常为空,数据全进了默认分区。
- 分区数不是越多越好——分区太多会导致元数据膨胀,查询优化器反而变慢。MySQL 官方建议单表分区不超过 8192 个,但我个人建议控制在 128 以内。
- 测试环境一定要模拟——分区表在数据量小的时候看不出优势,甚至可能更慢。一定要用生产级别的数据量做压测。