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
复合分区 两种方式组合 分层分布 混合查询 高 超大规模表

选哪种分区类型,没有标准答案。你得问自己三个问题:

  1. 查询条件是什么?——范围查询还是等值查询?
  2. 数据分布是否均匀?——有没有明显的数据倾斜?
  3. 数据量有多大?——单分区能不能控制在千万以内?

想清楚这三点,分区类型的选择就八九不离十了。

2.6 分区类型选择决策流程

下面这张图是我自己总结的决策流程,帮你快速定位该用哪种分区:

分区类型选择决策流程 开始选择分区类型 查询以范围查询为主? 是 单分区数据量 > 1000万? 是 推荐:复合分区 否 范围分区 否 等值查询为主? 是 枚举值有限且固定? 是 列表分区 否 哈希分区 决策要点说明 • 范围查询 → 优先考虑范围分区或复合分区 • 等值查询 + 枚举值固定 → 列表分区 • 等值查询 + 枚举值不固定 → 哈希分区 • 数据量超大 → 复合分区(范围+哈希 或 范围+列表)

这张图我用了很多年,每次做分区设计都拿出来对照一下。你照着走一遍,基本不会选错。

我的建议:刚开始做分区设计时,别追求一步到位。先用范围分区或列表分区跑起来,观察一段时间。如果发现数据倾斜严重或者查询性能不达标,再考虑升级到复合分区。分区表的好处是,你可以在线重建分区,不用停机。