4、分区表创建与管理:从建表到日常运维
分区表这东西,光会建是不够的。你得知道怎么建、怎么改、怎么修。我做了这么多年数据库,见过太多人建完分区就扔那儿不管了,结果数据一多,性能反而更差。今天咱们就把创建、维护、交换这几个核心操作捋一遍。
4.1 创建分区表:语法其实不复杂
先看最基础的——CREATE TABLE ... PARTITION BY。Oracle、MySQL、PostgreSQL 语法略有差异,但核心逻辑一样:告诉数据库按哪个列、用什么规则切分数据。
我个人习惯用范围分区(RANGE),因为它最直观。比如按时间切:
CREATE TABLE orders (
order_id NUMBER,
order_date DATE,
customer_id NUMBER,
amount NUMBER
)
PARTITION BY RANGE (order_date) (
PARTITION p_2023q1 VALUES LESS THAN (DATE '2023-04-01'),
PARTITION p_2023q2 VALUES LESS THAN (DATE '2023-07-01'),
PARTITION p_2023q3 VALUES LESS THAN (DATE '2023-10-01'),
PARTITION p_2023q4 VALUES LESS THAN (DATE '2024-01-01'),
PARTITION p_future VALUES LESS THAN (MAXVALUE)
);
注意最后那个 MAXVALUE 分区。我在项目中遇到过,有人忘了加这个兜底分区,结果 2024 年的数据插不进去,线上直接报错。嗯,这里要注意——兜底分区不是可选项,是必选项。
除了 RANGE,还有 LIST 和 HASH。LIST 适合按地区、状态等离散值切分:
PARTITION BY LIST (region) (
PARTITION p_north VALUES ('北京','天津','河北'),
PARTITION p_south VALUES ('广东','广西','海南'),
PARTITION p_other VALUES (DEFAULT)
);
HASH 则适合均匀打散数据,比如按用户 ID 哈希:
PARTITION BY HASH (customer_id) PARTITIONS 8;
三种方式怎么选?我列个表给你参考:
| 分区类型 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| RANGE | 时间、数值范围 | 易于管理历史数据,分区裁剪效果好 | 数据倾斜可能明显 |
| LIST | 离散值(地区、状态) | 语义清晰,按业务维度切分 | 分区数有限,新增值需维护 |
| HASH | 均匀分布、无自然分区键 | 数据分布均匀,避免热点 | 分区裁剪效果差,不适合范围查询 |
4.2 添加与删除分区:日常运维基本功
分区表建好之后,最频繁的操作就是加分区和删分区。你想想看,时间分区表每个月都要加新分区,历史数据也要定期清理。
添加分区:
-- 在末尾添加新分区
ALTER TABLE orders ADD PARTITION
PARTITION p_2024q1 VALUES LESS THAN (DATE '2024-04-01');
-- 在中间插入分区(需要先拆分)
ALTER TABLE orders SPLIT PARTITION p_future AT (DATE '2024-04-01')
INTO (PARTITION p_2024q1, PARTITION p_future);
删除分区:
ALTER TABLE orders DROP PARTITION p_2023q1;
删除分区时,数据会一起被删掉。我曾经见过有人手抖删错了分区,还好有备份。所以我的习惯是——删除前先查一下分区数据量,确认无误再执行。
4.3 合并与拆分分区:应对数据变化
业务变化了,分区策略也得跟着变。比如季度分区改成半年分区,或者某个分区数据太多需要拆细。
合并分区:
ALTER TABLE orders MERGE PARTITIONS p_2023q1, p_2023q2 INTO PARTITION p_2023h1;
合并后,两个分区的数据会归到新分区里。注意,合并后的分区边界取两个分区的最小值和最大值。
拆分分区:
ALTER TABLE orders SPLIT PARTITION p_2023h1 AT (DATE '2023-04-01')
INTO (PARTITION p_2023q1, PARTITION p_2023q2);
拆分时,AT 指定的是分割点。分割点左边的数据进第一个分区,右边的进第二个。我在项目中遇到过一个问题——拆分时如果分割点跟已有数据边界冲突,会报错。所以拆分前最好先确认一下数据分布。
4.4 分区交换:零停机数据迁移的利器
分区交换(Partition Exchange)是我最喜欢的功能。它可以在毫秒级别把一张普通表的数据“换”进分区,或者把分区数据“换”出来。
语法很简单:
-- 把普通表的数据换进分区
ALTER TABLE orders EXCHANGE PARTITION p_2023q1
WITH TABLE orders_staging;
-- 把分区数据换出到普通表
ALTER TABLE orders EXCHANGE PARTITION p_2023q1
WITH TABLE orders_archive;
交换的前提条件:
- 普通表的结构必须与分区表完全一致(列名、数据类型、索引等)
- 普通表的数据必须满足该分区的边界条件
- 交换操作不涉及数据复制,只修改数据字典
我曾经用分区交换做过一个历史数据归档方案:每个月把上个月的分区数据换到归档表,然后删除原分区。整个过程对业务完全透明,用户根本感觉不到。
VALIDATE 检查,确保数据不越界。否则交换会失败,而且错误信息不太友好。
4.5 知识体系总览
下面这张图把分区表创建与管理的核心操作串起来了。你可以把它当作日常运维的速查地图:
4.6 避坑指南:我踩过的几个坑
最后分享几个实战中容易翻车的地方:
- 分区键不能更新:一旦表按某列分区,就不能直接 UPDATE 该列的值。想改?只能删了重插。所以分区键选好后别轻易改。
- 全局索引会失效:对分区表做 DROP、TRUNCATE、SPLIT 等操作时,全局索引会变成 UNUSABLE。记得重建:
ALTER INDEX idx_name REBUILD。 - 分区数不是越多越好:我见过有人建了 1024 个分区,结果查询计划生成时间比执行时间还长。一般建议单表分区数不超过 100 个。
- MAXVALUE 分区要定期拆分:兜底分区数据会越积越多,如果不拆分,它就成了一个“伪分区表”,性能反而更差。