常见分区策略:范围、列表、哈希、复合

分区策略这东西,说白了就是决定「数据该去哪台机器」。

我见过不少团队,上来就拍脑袋选哈希分区,结果后面扩容时哭都哭不出来。选分区策略,得先搞清楚你的数据长什么样、查询怎么走。

下面这四种,是我在实际项目中用得最多的。咱们一个一个拆开看。

1. 范围分区(Range Partitioning)

按某个字段的连续区间来分。比如按时间、按ID范围。

核心逻辑: 数据按值的大小,落入不同的区间段。
-- 示例:按用户ID范围分区
CREATE TABLE users (
    id BIGINT,
    name VARCHAR(100),
    created_at TIMESTAMP
) PARTITION BY RANGE (id) (
    PARTITION p0 VALUES LESS THAN (1000000),
    PARTITION p1 VALUES LESS THAN (2000000),
    PARTITION p2 VALUES LESS THAN (3000000),
    PARTITION p3 VALUES LESS THAN MAXVALUE
);

适用场景:

  • 时间序列数据(日志、订单、监控指标)
  • 需要做范围扫描的查询(比如查某个月的数据)
  • 数据有明显冷热分层(旧数据归档)

优点:

  • 范围查询效率极高,直接裁剪掉无关分区
  • 分区管理直观,加分区、删分区都很方便
  • 数据局部性好,相邻数据大概率在同一分区

缺点:

  • 容易产生数据倾斜——热点数据全挤在一个分区
  • 跨分区查询需要合并结果,性能下降
我的经验: 做时间范围分区时,我习惯把分区粒度设成「天」或「小时」。太粗了热点集中,太细了分区数爆炸。曾经有个日志系统,我按分钟分区,结果一天产生1440个分区,元数据管理直接崩了。

2. 列表分区(List Partitioning)

按离散的枚举值来分。比如按地区、按状态、按业务线。

-- 示例:按地区分区
CREATE TABLE orders (
    order_id BIGINT,
    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 ('其他')
);

适用场景:

  • 数据有明确的分类属性
  • 查询经常按某个枚举字段过滤
  • 多租户场景,按租户ID分区

优点:

  • 语义清晰,一眼就能看出数据归属
  • 管理方便,增删分区不影响其他分区
  • 适合做数据隔离(比如不同租户物理隔离)

缺点:

  • 枚举值分布不均时,照样倾斜
  • 新增枚举值需要手动加分区
避坑指南: 我曾经在一个电商项目里,用「省份」做列表分区。结果双十一期间,广东、浙江几个大省的单量是其他省的几十倍。那几个分区直接被打满,其他分区却闲着。后来我改成按「省份+用户ID哈希」的复合分区,才把负载摊平。

3. 哈希分区(Hash Partitioning)

对分区键做哈希计算,然后模上分区数,决定数据去哪。

-- 示例:按用户ID哈希分区(4个分区)
CREATE TABLE users (
    id BIGINT,
    name VARCHAR(100)
) PARTITION BY HASH (id) PARTITIONS 4;

-- 数据分布逻辑(伪代码)
partition_id = hash(user_id) % 4

适用场景:

  • 数据分布需要尽量均匀
  • 没有明显的范围查询需求
  • 写操作频繁,需要分散写入压力

优点:

  • 数据分布最均匀,基本不会倾斜
  • 写入吞吐高,各分区压力均衡
  • 适合做负载均衡的基础策略

缺点:

  • 范围查询基本废了,得扫全部分区
  • 扩容时数据需要大量迁移(分区数变了,哈希结果全变)
  • 分区数一旦确定,改起来很痛苦
我的建议: 哈希分区数最好设成2的幂次(比如8、16、32)。这样扩容时可以用「一致性哈希」或「虚拟桶」来减少迁移量。我有个项目一开始设了7个分区,后来加节点时,数据迁移量接近100%,差点没把运维累死。

4. 复合分区(Composite Partitioning)

把上面几种策略组合起来用。先做一层分区,再在内部做第二层分区。

-- 示例:先按时间范围分区,再按用户ID哈希子分区
CREATE TABLE orders (
    order_id BIGINT,
    user_id BIGINT,
    created_at TIMESTAMP
) PARTITION BY RANGE (YEAR(created_at))
SUBPARTITION BY HASH (user_id) SUBPARTITIONS 4 (
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p2024 VALUES LESS THAN (2025),
    PARTITION p2025 VALUES LESS THAN (2026)
);

适用场景:

  • 既要范围裁剪,又要写入均衡
  • 数据量大到单层分区不够用
  • 需要同时满足多种查询模式

常见组合:

组合方式 第一层 第二层 典型场景
范围-哈希 时间范围 用户ID哈希 订单系统,按时间归档+写入均衡
范围-列表 时间范围 地区列表 多区域日志,按时间+区域查询
列表-哈希 租户列表 业务ID哈希 多租户SaaS,租户隔离+内部均衡
哈希-范围 用户ID哈希 时间范围 用户维度的时序数据

优点:

  • 灵活性最高,能应对复杂场景
  • 兼顾范围查询和写入均衡
  • 数据管理粒度更细

缺点:

  • 元数据复杂,管理成本高
  • 查询优化器不一定能充分利用两层裁剪
  • 运维操作(加分区、合并分区)更繁琐
核心原则: 复合分区不是越多层越好。两层基本够用,三层以上我建议你重新审视设计。每多一层,查询计划和元数据管理的复杂度就翻一倍。

四种策略对比一览

策略 数据分布 范围查询 写入均衡 扩容难度 典型场景
范围分区 可能倾斜 优秀 差 低 时间序列、日志
列表分区 可能倾斜 中等 中等 低 多租户、分类数据
哈希分区 均匀 差 优秀 高 高并发写入、缓存
复合分区 可控 良好 良好 中 复杂业务、海量数据

分区策略选择决策流程

下面这张图,是我自己总结的选型思路。每次做分区设计,我都会过一遍这个流程。

分区策略选择决策流程 数据需要分区吗? 有明确的范围查询吗? 是 否 范围分区 按时间/ID范围 数据有分类属性吗? 是 否 列表分区 按地区/状态/租户 哈希分区 按ID/Key哈希 需要兼顾多种查询模式? 是 否 复合分区 范围+哈希 / 范围+列表 保持单层分区 当前策略已够用

嗯,这个流程我用了好几年。每次做分区设计,先问自己三个问题:

  1. 查询模式是什么? 范围查询多,还是点查多?
  2. 写入压力在哪? 有没有热点?要不要均衡?
  3. 未来怎么扩容? 分区数能变吗?迁移成本能接受吗?
我的血泪教训: 曾经有个金融项目,一开始只用了哈希分区。后来业务方要求按时间范围查历史订单,每次都得扫全部分区,慢得要命。最后不得不改成复合分区,数据迁移搞了整整一个周末。所以,分区策略一定要提前想清楚查询模式,不然后面改起来成本极高。

四种策略没有的好坏。关键看你的数据特征和业务需求。我个人习惯是:

  • 时序数据 → 范围分区
  • 多租户 → 列表分区
  • 高并发写入 → 哈希分区
  • 复杂场景 → 复合分区

记住一点:分区是手段,不是目的。别为了分区而分区,搞得系统复杂得没法维护。