2、分区类型详解:范围分区、列表分区、哈希分区、复合分区
分区表的核心,说白了就是「分而治之」。把一张大表拆成多个小表,查询时只扫需要的分区,性能自然就上来了。但怎么拆?拆成什么样?这就涉及到分区类型的选择了。
我做了这么多年数据库优化,见过不少因为分区类型选错导致性能反而下降的案例。今天咱们就把四种主流分区类型掰开揉碎讲清楚。
2.1 范围分区(RANGE Partitioning)
范围分区是最常用的一种。按某个列的值的范围来划分,比如按日期、按ID区间。
适用场景:数据有明显的时间维度,或者有自然的分段区间。
举个例子,一张订单表,按月份分区:
CREATE TABLE orders (
order_id INT,
order_date DATE,
customer_id INT,
amount DECIMAL(10,2)
)
PARTITION BY RANGE (YEAR(order_date)) (
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
这里有个坑,我踩过。最后一个分区一定要用 MAXVALUE 兜底,否则新数据插不进去。我曾经在生产环境上忘了加,结果业务高峰期直接报错,那叫一个狼狈。
范围分区的优势很明显:
- 分区裁剪效率高——查询条件带范围时,能精准定位到少数分区
- 管理方便——旧数据可以直接 truncate 掉整个分区
- 适合归档——按月、按季度切分,数据生命周期管理很自然
但也要注意,如果范围定义不合理,比如某个分区数据量特别大,其他分区很小,那就失去了分区的意义。你想想看,分区就是为了均衡,结果一个分区占了80%的数据,那还不如不分。
2.2 列表分区(LIST Partitioning)
列表分区是按离散值来划分的。比如按地区、按状态、按类型。说白了,就是枚举值分区。
CREATE TABLE orders (
order_id INT,
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 ('其他')
);
我个人习惯用列表分区来处理「枚举值分布均匀」的场景。比如订单状态:待支付、已支付、已发货、已完成、已取消。每个状态的数据量差不多,查询时按状态过滤,效果很好。
小技巧:列表分区一定要考虑「兜底值」。如果业务新增了一个枚举值,而分区定义里没有,插入就会报错。我建议预留一个 DEFAULT 或 OTHER 分区。
列表分区和范围分区最大的区别是什么?范围分区是连续的区间,列表分区是离散的点。选哪个,取决于你的业务数据是连续的还是离散的。
2.3 哈希分区(HASH Partitioning)
哈希分区就不一样了。它不是按业务语义来分,而是按哈希函数算出来的值来分。目的是让数据均匀分布。
CREATE TABLE orders (
order_id INT,
customer_id INT,
amount DECIMAL(10,2)
)
PARTITION BY HASH (customer_id)
PARTITIONS 8;
这里 PARTITIONS 8 表示分成8个分区。哈希分区的好处是数据自动均匀分布,不需要你操心哪个分区放什么数据。
但有个问题——分区数一旦定下来,后面很难改。我遇到过一位同事,一开始分了4个分区,后来数据量涨了10倍,想改成16个分区,结果发现要重建整个表。嗯,这里要注意,哈希分区的分区数最好是2的幂次方,比如4、8、16、32,这样哈希分布更均匀。
避坑指南:哈希分区不适合范围查询。因为查询条件没法直接映射到具体分区,数据库得扫描所有分区。我曾经接手过一个项目,用哈希分区做时间范围查询,结果全表扫描,性能还不如不分。
哈希分区最适合的场景是什么?等值查询。比如按用户ID查订单,哈希到具体分区,直接命中,速度飞快。
2.4 复合分区(Composite Partitioning)
复合分区,就是上面几种分区的组合。先按一种方式分区,再在每个分区内按另一种方式子分区。说白了,就是「分区套分区」。
最常见的组合是 RANGE-HASH 和 RANGE-LIST。
CREATE TABLE orders (
order_id INT,
order_date DATE,
region VARCHAR(20),
amount DECIMAL(10,2)
)
PARTITION BY RANGE (YEAR(order_date))
SUBPARTITION BY HASH (order_id)
SUBPARTITIONS 4 (
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024)
);
这个例子,先按年份范围分区,再在每个年份分区内按订单ID哈希分成4个子分区。这样既支持按时间范围裁剪,又保证了数据在子分区内的均匀分布。
我个人的经验是,复合分区适合「大表中的大表」。比如一张10亿级的订单表,单用范围分区,每个分区可能还有几千万条,查询还是慢。这时候再加一层哈希子分区,把每个范围分区再拆成8个、16个子分区,效果立竿见影。
但复合分区也有代价:
- 管理复杂度翻倍——分区和子分区都要维护
- 元数据膨胀——分区信息占用的内存更多
- DDL操作更慢——加分区、删分区涉及子分区的重建
所以我的建议是:能用单层分区解决的问题,别上复合分区。只有单层分区后单个分区数据量仍然超过千万级别,才考虑复合分区。
2.5 四种分区类型对比
为了方便你快速决策,我整理了一张对比表:
| 分区类型 | 划分依据 | 数据分布 | 适用查询 | 管理难度 | 典型场景 |
|---|---|---|---|---|---|
| 范围分区 | 连续区间 | 按区间分布 | 范围查询 | 低 | 时间序列、日志 |
| 列表分区 | 离散枚举值 | 按枚举分布 | 等值查询 | 低 | 地区、状态、类型 |
| 哈希分区 | 哈希函数 | 均匀分布 | 等值查询 | 中 | 用户ID、订单ID |
| 复合分区 | 两种方式组合 | 分层分布 | 混合查询 | 高 | 超大规模表 |
选哪种分区类型,没有标准答案。你得问自己三个问题:
- 查询条件是什么?——范围查询还是等值查询?
- 数据分布是否均匀?——有没有明显的数据倾斜?
- 数据量有多大?——单分区能不能控制在千万以内?
想清楚这三点,分区类型的选择就八九不离十了。
2.6 分区类型选择决策流程
下面这张图是我自己总结的决策流程,帮你快速定位该用哪种分区:
这张图我用了很多年,每次做分区设计都拿出来对照一下。你照着走一遍,基本不会选错。
我的建议:刚开始做分区设计时,别追求一步到位。先用范围分区或列表分区跑起来,观察一段时间。如果发现数据倾斜严重或者查询性能不达标,再考虑升级到复合分区。分区表的好处是,你可以在线重建分区,不用停机。