一、分区表基础概念
各位同学好,我是老张。今天咱们聊聊分区表。这东西说白了,就是把一张大表拆成多个小表来管理。我刚开始接触分区表时,也觉得不就是分拆嘛,有啥难的?后来在生产环境里栽过跟头,才明白这里面的门道有多深。
先问大家一个问题:一张表里存了 10 亿行数据,你做个全表扫描,得多久?我见过最夸张的一次,某电商的订单表跑了将近 40 分钟。后来改成按月份分区,查询时间直接降到秒级。这就是分区表的价值。
1.1 什么是分区表
分区表,是一种将逻辑上的一张表,物理上拆分成多个独立存储单元的技术。每个存储单元叫一个「分区」。对应用程序来说,它看到的还是一张表,SQL 不用改。但对数据库来说,数据是分散存储的。
举个例子。你有一张订单表 orders,按月份分成 12 个分区。查询 1 月份的数据时,数据库只扫描 1 月那个分区,其他 11 个分区碰都不碰。性能自然就上去了。
核心要点:分区表 = 逻辑统一 + 物理分散。应用层无感知,但底层存储和查询可以并行、裁剪、隔离。
我个人习惯把分区表比作「档案柜」。一张大表就是一个大柜子,里面塞满了文件。找一份文件得翻遍整个柜子。分区表呢,相当于给柜子加了隔层——1 月文件放第一层,2 月放第二层……找文件时直接打开对应那层就行。
1.2 分区键
分区键,就是决定数据「该放哪个分区」的列。它可以是单列,也可以是多列的组合。
选分区键是个技术活。我在项目中遇到过好几次,因为分区键选得不好,导致数据分布严重不均。有的分区塞得满满当当,有的分区几乎空着。这叫什么?这叫「数据倾斜」。分区表最怕这个。
| 分区键类型 | 示例 | 适用场景 |
|---|---|---|
| 时间列 | order_date |
日志、订单、流水等时序数据 |
| 地域列 | region_id |
按区域划分的业务数据 |
| 业务ID | user_id |
用户数据分片 |
| 枚举列 | status |
状态有限的业务数据 |
选分区键的经验:优先选查询条件中频繁出现的列。如果查询经常带 WHERE order_date BETWEEN ...,那分区键就选 order_date。这样能最大化「分区裁剪」的效果。
1.3 分区类型
常见的分区类型有四种:范围分区、列表分区、哈希分区、复合分区。每种都有各自的脾气,用错了会很难受。
1.3.1 范围分区(RANGE)
范围分区,是按分区键的值范围来划分。最典型的就是按时间分区。
CREATE TABLE orders (
order_id BIGINT,
order_date DATE,
amount DECIMAL(10,2)
)
PARTITION BY RANGE (order_date) (
PARTITION p202401 VALUES LESS THAN ('2024-02-01'),
PARTITION p202402 VALUES LESS THAN ('2024-03-01'),
PARTITION p202403 VALUES LESS THAN ('2024-04-01'),
PARTITION p_future VALUES LESS THAN (MAXVALUE)
);
注意最后那个 MAXVALUE 分区。我曾经吃过这个亏——没加它,结果有一条数据日期超出了范围,插入直接报错。生产环境半夜报警,那叫一个酸爽。
范围分区的优点很明显:数据按顺序分布,范围查询效率高。缺点呢?如果数据分布不均匀,比如某个月订单量暴增,那个分区就会成为热点。
1.3.2 列表分区(LIST)
列表分区,是按分区键的值列表来划分。适合枚举值固定的场景。
CREATE TABLE orders_region (
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 ('其他')
);
列表分区有个坑:如果插入的数据不在任何列表里,会直接报错。所以最好加一个 DEFAULT 分区兜底。嗯,这里要注意,MySQL 8.0 开始支持 DEFAULT 分区,但 Oracle 的语法略有不同。
1.3.3 哈希分区(HASH)
哈希分区,是对分区键做哈希运算,然后按哈希值模分区数来分配数据。它不关心数据本身的值,只关心哈希结果。
CREATE TABLE orders_hash (
order_id BIGINT,
user_id BIGINT,
amount DECIMAL(10,2)
)
PARTITION BY HASH (user_id)
PARTITIONS 8;
哈希分区的最大好处是数据分布均匀。你想想看,哈希函数天然就是打散的。但缺点也很明显:范围查询基本用不上分区裁剪,因为相邻的值可能被哈希到不同的分区。
我建议哈希分区用在「等值查询」为主的场景。比如按用户 ID 查订单,哈希分区就很合适。
1.3.4 复合分区(COMPOSITE)
复合分区,就是先按一种方式分区,再在每个分区内按另一种方式子分区。说白了就是「分区套分区」。
CREATE TABLE orders_composite (
order_id BIGINT,
order_date DATE,
region VARCHAR(20),
amount DECIMAL(10,2)
)
PARTITION BY RANGE (order_date)
SUBPARTITION BY LIST (region)
SUBPARTITION TEMPLATE (
SUBPARTITION s_north VALUES IN ('北京', '天津', '河北'),
SUBPARTITION s_south VALUES IN ('广东', '广西', '海南'),
SUBPARTITION s_east VALUES IN ('上海', '江苏', '浙江'),
SUBPARTITION s_other VALUES IN ('其他')
) (
PARTITION p202401 VALUES LESS THAN ('2024-02-01'),
PARTITION p202402 VALUES LESS THAN ('2024-03-01'),
PARTITION p202403 VALUES LESS THAN ('2024-04-01')
);
复合分区的灵活性很高。比如先按月份范围分区,再按地区列表子分区。这样既能做时间范围裁剪,又能做地区过滤。但代价是分区数量会爆炸——3 个月份 × 4 个地区 = 12 个分区。如果分区数太多,管理成本会直线上升。
注意:不是所有数据库都支持复合分区。MySQL 8.0 支持 RANGE + LIST、RANGE + HASH、LIST + HASH 等组合。但 PostgreSQL 的复合分区支持相对有限。做迁移前一定要确认目标库的能力边界。
1.4 四种分区类型对比
| 分区类型 | 数据分布方式 | 分区裁剪效果 | 数据均匀性 | 典型场景 |
|---|---|---|---|---|
| RANGE | 按值范围 | 范围查询极好 | 可能不均匀 | 时间序列数据 |
| LIST | 按值列表 | 等值查询好 | 取决于枚举分布 | 地域、状态分类 |
| HASH | 按哈希值 | 等值查询好 | 非常均匀 | 用户ID分片 |
| COMPOSITE | 多层组合 | 多层裁剪 | 取决于组合方式 | 复杂业务场景 |
1.5 分区表知识体系总览
下面这张图,把分区表的核心概念串起来了。我建议你多看几遍,脑子里先搭好框架。
这张图把分区表的核心脉络理清楚了。从上往下看:分区表依赖分区键来划分数据,分区类型决定了划分的规则,分区裁剪则是性能收益的来源。四种分区类型各有侧重,选型时要结合业务场景。
我的建议:刚开始接触分区表,先从 RANGE 分区入手。它最直观,也最容易理解。等用熟了,再尝试 LIST 和 HASH。复合分区嘛,等你有了一定经验再碰,不然容易把自己绕进去。