2、分区类型详解(上):范围分区、列表分区、哈希分区
分区表的核心,说白了就是「分而治之」。把一张大表拆成若干小表,查询时只扫相关分区,性能自然就上来了。但怎么拆,拆成什么样,这里头门道不少。
我个人习惯把分区类型分成两类:逻辑分区和数据分布分区。范围分区和列表分区属于前者——分区规则跟业务逻辑强相关;哈希分区属于后者——它只管把数据均匀打散,不关心数据本身是什么。
今天我们先聊这三种最基础的分区类型。嗯,先看一张总览图,心里有个谱。
2.1 范围分区(RANGE)
范围分区是最常用、也最直观的一种。你指定一个列作为分区键,然后按值的区间把数据切到不同分区里。
举个例子,订单表按月份分区:
CREATE TABLE orders (
order_id BIGINT,
order_date DATE,
customer_id INT,
amount DECIMAL(10,2)
)
PARTITION BY RANGE (order_date) (
PARTITION p_2024_q1 VALUES LESS THAN ('2024-04-01'),
PARTITION p_2024_q2 VALUES LESS THAN ('2024-07-01'),
PARTITION p_2024_q3 VALUES LESS THAN ('2024-10-01'),
PARTITION p_2024_q4 VALUES LESS THAN ('2025-01-01')
);
这里有个细节:VALUES LESS THAN 定义的是上界,不包含边界值。也就是说,2024-03-31 的数据进 p_2024_q1,2024-04-01 的数据进 p_2024_q2。
MAXVALUE 兜底。我个人倾向提前建,因为 MAXVALUE 分区一旦数据量大了,拆分起来挺麻烦的。
范围分区的优势很明显:
- 分区裁剪效果好——查询条件带分区键范围时,优化器直接跳过无关分区。比如
WHERE order_date BETWEEN '2024-05-01' AND '2024-06-30',只扫 p_2024_q2。 - 方便数据生命周期管理——旧分区可以直接
DROP或归档,比 DELETE 快几个数量级。 - 范围查询友好——按时间、ID 范围查数据的场景,天然适配。
但也有坑:
- 数据倾斜——如果某个时间段数据量暴增(比如双十一),那个分区会特别大,失去分区优势。
- 分区键选择要谨慎——别选那种频繁更新的列做分区键,否则每次更新都可能跨分区移动数据,性能灾难。
2.2 列表分区(LIST)
列表分区跟范围分区不同,它不是按连续区间,而是按离散值列表来划分。你指定一个列,然后告诉数据库:「这些值放这个分区,那些值放那个分区」。
最适合的场景:地区、状态、类别等枚举值。
CREATE TABLE orders (
order_id BIGINT,
region VARCHAR(20),
order_date DATE,
amount DECIMAL(10,2)
)
PARTITION BY LIST (region) (
PARTITION p_north VALUES IN ('北京', '天津', '河北', '山西'),
PARTITION p_east VALUES IN ('上海', '江苏', '浙江', '安徽'),
PARTITION p_south VALUES IN ('广东', '福建', '海南'),
PARTITION p_other VALUES IN ('default')
);
注意最后那个 p_other 分区,我用了一个 default 关键字。这相当于兜底——所有没匹配到的值都扔进去。嗯,这个设计很关键。
ERROR: no partition of relation "orders" found for row。生产环境半夜出这种问题,够你喝一壶的。
列表分区的适用场景:
- 按地域隔离数据——不同地区的数据可以存在不同分区,方便按地区查询和管理。
- 按状态分类——比如订单状态:待支付、已支付、已发货、已完成、已取消。每个状态一个分区,查询特定状态时效率很高。
- 多租户场景——不同租户的数据分到不同分区,租户隔离天然实现。
需要注意的点:
- 列表值不能重叠——同一个值不能出现在两个分区的 IN 列表里。
- 分区数量不宜过多——我建议控制在 50 个以内。太多分区会导致元数据膨胀,DDL 操作变慢。
- default 分区要监控——它很容易变成「垃圾堆」,所有未预期的值都往里塞,最后这个分区可能比所有正常分区都大。
2.3 哈希分区(HASH)
哈希分区跟前面两种完全不同。它不关心数据本身的业务含义,而是对分区键做哈希运算,然后根据哈希值模分区数,决定数据落到哪个分区。
说白了,它的目标只有一个:让数据均匀分布。
CREATE TABLE user_session (
session_id VARCHAR(64),
user_id BIGINT,
login_time TIMESTAMP,
ip_address VARCHAR(45)
)
PARTITION BY HASH (user_id)
PARTITIONS 8;
这里 PARTITIONS 8 表示分成 8 个分区。数据库内部对 user_id 做哈希,然后模 8,结果 0-7 分别对应 8 个分区。
哈希分区的核心价值:
- 数据均匀分布——只要分区键的基数够大,数据基本能均匀散到各个分区。不会出现范围分区那种「一个分区撑死,其他分区饿死」的情况。
- 适合随机访问——按分区键做等值查询时,直接定位到单个分区,性能很好。
- 避免热点——写操作可以分散到多个分区,不会集中在某一个分区上。
但哈希分区也有明显的短板:
- 范围查询不友好——比如
WHERE user_id BETWEEN 1000 AND 2000,这个范围的数据可能散落在所有 8 个分区里,每个分区都得扫一遍。 - 分区数一旦确定,改起来很麻烦——哈希分区数变了,所有数据都得重新计算哈希、重新分布。虽然数据库可以自动做,但代价很大。
- 分区键选择要谨慎——如果分区键的基数很小(比如性别,只有男、女),哈希出来也就两种结果,分 8 个分区等于白分。
2.4 三种分区类型的对比
三种分区各有各的脾气,选哪个得看业务场景。我整理了一张对比表,方便你快速决策:
| 维度 | RANGE 范围分区 | LIST 列表分区 | HASH 哈希分区 |
|---|---|---|---|
| 分区依据 | 连续区间 | 离散值列表 | 哈希函数 |
| 数据分布 | 可能倾斜 | 可能倾斜 | 均匀分布 |
| 范围查询 | ✅ 高效 | ❌ 不适用 | ❌ 低效 |
| 等值查询 | ✅ 高效(定位到分区) | ✅ 高效 | ✅ 高效 |
| 分区管理 | ✅ 方便增删 | ✅ 方便增删 | ⚠️ 改分区数代价大 |
| 典型场景 | 时间、ID 范围 | 地区、状态、枚举 | 均匀散列、无业务倾向 |
| 分区键要求 | 有序、单调递增 | 离散、有限枚举 | 基数大、分布均匀 |
2.5 选型决策思路
你可能会问:那我到底该用哪种?
我的经验是三步走:
- 先看有没有天然的业务分区键——比如时间、地区、状态。有的话,优先考虑 RANGE 或 LIST。
- 再看查询模式——如果查询经常带范围条件,选 RANGE;如果经常做等值查询且分区键是枚举值,选 LIST;如果查询模式随机,没有明显规律,选 HASH。
- 最后看数据增长模式——数据持续增长且需要定期清理旧数据的,RANGE 最合适;数据量稳定但需要均匀分散的,HASH 更靠谱。
好了,三种基础分区类型就聊到这儿。范围分区像切蛋糕,按时间或 ID 一刀一刀切;列表分区像分拣快递,按目的地归类;哈希分区像摇骰子,随机但均匀。没有最好的分区类型,只有最适合你业务场景的。