分区类型详解:范围分区、列表分区、哈希分区、复合分区

分区表的核心,说白了就是「分而治之」。把一张大表拆成多个物理小段,查询时只扫相关分区,性能自然就上来了。但分区类型怎么选?这问题我几乎每次做架构评审都会被问到。

今天咱们就把四种主流分区类型掰开揉碎讲清楚。我会结合自己踩过的坑,帮你建立选型直觉。

范围分区(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日当天的数据全进了下一个分区,排查了半天。

小技巧: 范围分区建议预留一个 MAXVALUE 分区,防止数据溢出导致插入失败。但记得定期清理或拆分,否则 MAXVALUE 分区会越来越大,失去分区优势。

列表分区(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,运维成本不低。

避坑指南: 列表分区一定要用 DEFAULT 分区兜底。我曾经接手过一个系统,没有 DEFAULT 分区,新来的数据因为找不到匹配分区直接报错,业务停了半小时。

哈希分区(Hash Partitioning)

哈希分区不关心数据本身的业务含义,而是对分区键做哈希运算,均匀分布到N个分区。适合数据分布不均匀、没有明显范围或列表特征的场景。

CREATE TABLE session_logs (
    session_id  VARCHAR(64),
    event_time  TIMESTAMP,
    payload     TEXT
)
PARTITION BY HASH (session_id)
PARTITIONS 8;

哈希分区的核心优势是数据均匀分布。但代价是——你没法做分区裁剪。查询时必须扫描所有分区,除非 where 条件里精确匹配了分区键。我记得有一次做性能优化,发现哈希分区表上跑范围查询,反而比普通表还慢,就是因为每个分区都要扫一遍。

分区数怎么定? 一般建议 2 的幂次方:4、8、16、32。这样哈希取模时性能最好。而且分区数最好一次定好,后期扩分区需要重建表,代价很大。

复合分区(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')
);

这个例子很典型:按时间范围做一级分区,方便数据归档;再按指标名称做哈希子分区,保证同一时间范围内数据均匀分布。我在监控系统里用过这种方案,查询某段时间内所有指标时,先裁剪到对应时间分区,再并行扫描子分区,性能提升非常明显。

注意: 复合分区不是所有数据库都支持。MySQL 8.0 支持 RANGE + HASH、RANGE + KEY、LIST + HASH、LIST + KEY 四种组合。但 RANGE + LIST 这种组合很多数据库不支持,选型前一定要确认。

各类型对比与选型

下面这张表是我自己整理的选型对照表,每次做架构设计都会拿出来参考。

分区类型 数据分布 分区裁剪 维护成本 典型场景
范围分区 按连续区间 优秀(范围查询) 低(定期加分区即可) 日志、订单、时序数据
列表分区 按离散值 优秀(等值查询) 中(枚举变化需维护) 地区、状态、分类
哈希分区 均匀分布 差(仅等值匹配) 高(扩分区需重建) 会话、流水、随机ID
复合分区 分层分布 优秀(一级裁剪) 高(两层管理) 大规模时序+多维查询

选型决策流程

我一般按下面这个流程来选型,你可以参考一下。

  1. 先看查询模式——如果查询经常带范围条件,优先范围分区;如果是等值查询,列表或哈希都可以。
  2. 再看数据分布——数据有明显的时间或序列特征?用范围。数据离散且均匀?用哈希。数据有固定分类?用列表。
  3. 考虑维护成本——团队运维能力有限?别碰复合分区。数据量极大且需要归档?范围分区最省心。
  4. 最后看数据库能力——有些数据库不支持复合分区,有些对分区数有限制。先查文档再动手。
我的建议: 80% 的场景用范围分区就够了。别为了炫技上复合分区,维护成本翻倍不说,查询收益未必明显。先简单,再优化。

分区类型关系图

下面这张图展示了四种分区类型的关系和选型路径,帮你快速建立整体认知。

分区类型选型决策图 分区表 单层分区 复合分区 范围分区 列表分区 哈希分区 范围+哈希 列表+哈希 选型建议 • 时间序列数据 → 范围分区(最简单、最常用) • 固定枚举值 → 列表分区(地区、状态、分类) • 均匀分布需求 → 哈希分区(会话、流水号) • 大规模多维查询 → 复合分区(时间+业务维度)

选型避坑总结

最后,我把自己这些年踩过的坑总结成几条原则,你记一下。

  • 别过度设计——能用范围分区解决的问题,别上复合分区。多一层分区多一层维护成本。
  • 分区键要稳定——分区键一旦选定,后期修改代价极大。我曾经见过有人用「创建时间」做分区键,结果业务改了数据写入逻辑,时间字段经常为空,数据全进了默认分区。
  • 分区数不是越多越好——分区太多会导致元数据膨胀,查询优化器反而变慢。MySQL 官方建议单表分区不超过 8192 个,但我个人建议控制在 128 以内。
  • 测试环境一定要模拟——分区表在数据量小的时候看不出优势,甚至可能更慢。一定要用生产级别的数据量做压测。
一句话总结: 范围分区是基础,列表分区是补充,哈希分区是特例,复合分区是进阶。从简单开始,按需演进。