OceanBase 分区表指定
一、分区表的基本概念
以按月分区的订单表为例,应用看到的是一张逻辑表 orders:
text
应用看到的一张逻辑表:orders
│
┌──────────┼──────────┐
↓ ↓ ↓
1 月分区 2 月分区 3 月分区
存 1 月数据 存 2 月数据 存 3 月数据
应用始终使用同一个表名进行查询:
sql
SELECT *
FROM orders
WHERE created_at >= '2026-02-01'
AND created_at < '2026-03-01';
数据库根据查询条件判断只需要访问 2 月分区,这个过程称为分区裁剪(Partition Pruning)。
严格来说,不宜把每个分区简单称为"一张物理表":分区是同一张表的数据组成部分,不是应用需要分别操作的独立业务表。在 OceanBase 中,分区可以分布到不同的 OBServer,并按照配置维护多个副本。
可以这样理解:
| 概念 | 含义 |
|---|---|
| 表 | 应用访问的完整数据集合 |
| 分区 | 将表中的数据按规则拆成不同部分 |
| 副本 | 为同一部分数据保存多份冗余 |
分区是"拆开存",副本是"复制存"。
二、建表时指定分区
在 OceanBase MySQL 兼容模式下,建表时在 CREATE TABLE 语句后增加 PARTITION BY,即可指定分区规则。
建议使用 orders 作为表名,因为 ORDER 是 SQL 关键字。如果必须使用 order,需要用反引号包裹:order。
分区键应根据主要查询条件和数据管理方式选择。下面给出两种常见方案,通常需要结合业务场景二选一。
方案一:按订单 ID 使用 HASH 分区
如果主要按订单 ID 查询,并且希望将数据均匀分散到多个分区,可以使用 HASH 分区:
sql
CREATE TABLE orders (
id BIGINT NOT NULL AUTO_INCREMENT,
user_id BIGINT NOT NULL,
amount DECIMAL(18, 2) NOT NULL,
status TINYINT NOT NULL DEFAULT 0,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_user_created (user_id, created_at)
)
PARTITION BY HASH (id)
PARTITIONS 8;
关键部分如下:
sql
PARTITION BY HASH (id) -- 使用 id 作为分区键
PARTITIONS 8 -- 建立 8 个分区
这里的 8 只是示例,不代表所有场景都应该使用 8 个分区。分区数量应结合预计数据量、租户资源和访问模式确定,也不需要等于 Zone 数量。
插入和查询
应用不需要指定分区,数据库会根据分区规则决定数据落在哪个分区:
sql
INSERT INTO orders (user_id, amount)
VALUES (1001, 99.90);
按 ID 查询时,数据库可以根据 ID 定位对应分区:
sql
SELECT *
FROM orders
WHERE id = 12345;
不同查询条件对 HASH 分区裁剪的影响如下:
| 查询条件 | 对该分区设计的影响 |
|---|---|
id = 12345 |
可以定位对应分区 |
id IN (12345, 12346) |
可以定位这些 ID 对应的分区 |
user_id = 1001 |
不能仅凭该条件裁剪 ID 分区;即使使用局部索引,也可能涉及多个分区 |
id > 12345 |
HASH 分区不按 ID 连续范围排列,通常仍需访问多个分区 |
| 按时间范围查询 | 不能仅凭时间条件裁剪 ID 分区 |
因此,HASH 分区有助于分散数据和访问压力,但不意味着所有查询都会更快。
方案二:按时间使用 RANGE 分区
如果主要按月份查询、归档和清理历史订单,可以使用 RANGE 分区:
sql
CREATE TABLE orders (
id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
amount DECIMAL(18, 2) NOT NULL,
status TINYINT NOT NULL DEFAULT 0,
created_at DATETIME NOT NULL,
PRIMARY KEY (created_at, id)
)
PARTITION BY RANGE COLUMNS (created_at) (
PARTITION p_before_202609
VALUES LESS THAN ('2026-09-01'),
PARTITION p202609
VALUES LESS THAN ('2026-10-01'),
PARTITION p202610
VALUES LESS THAN ('2026-11-01'),
PARTITION p_future
VALUES LESS THAN (MAXVALUE)
);
对应的时间范围如下:
| 分区 | 时间范围 |
|---|---|
p_before_202609 |
2026-09-01 之前 |
p202609 |
2026-09-01 至 2026-10-01,不含结束时刻 |
p202610 |
2026-10-01 至 2026-11-01,不含结束时刻 |
p_future |
2026-11-01 及以后 |
按时间范围查询时,可以裁剪到对应分区:
sql
SELECT *
FROM orders
WHERE created_at >= '2026-09-01'
AND created_at < '2026-10-01';
该方案的注意事项
PRIMARY KEY (created_at, id)只保证组合唯一,不单独保证id全表唯一。如果需要数据库强制保证 ID 全局唯一,应结合目标 OceanBase 版本和索引类型设计全局唯一索引。- 需要定期维护后续时间分区,例如在新月份到来前新增分区。
- 当前写入通常会集中在最新分区,需要结合业务写入量评估热点问题。
- 分区键出现在主键中,是为了满足分区表主键设计约束;实际建表前仍应确认目标 OceanBase 版本的语法和限制。
三、应用如何访问分区表
应用通常只访问逻辑表名,不需要直接操作分区名:
sql
INSERT INTO orders (id, user_id, amount, status, created_at)
VALUES (1000001, 1001, 99.90, 0, '2026-09-15 10:30:00');
SELECT *
FROM orders
WHERE created_at >= '2026-09-01'
AND created_at < '2026-10-01';
数据库会根据插入数据的分区键进行路由,并根据查询条件执行分区裁剪。
为了让分区裁剪更容易生效,时间范围查询建议使用左闭右开形式:
sql
created_at >= '开始时间'
AND created_at < '结束时间'
例如按月查询时使用"当月第一天(含)到下月第一天(不含)",避免使用依赖时间精度的 23:59:59。
四、如何确认建表结果
查看完整建表语句:
sql
SHOW CREATE TABLE orders;
也可以查询分区元数据:
sql
SELECT
TABLE_NAME,
PARTITION_NAME,
PARTITION_METHOD,
PARTITION_EXPRESSION,
PARTITION_DESCRIPTION,
TABLE_ROWS
FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'orders'
ORDER BY PARTITION_ORDINAL_POSITION;
五、分区与副本的职责边界
分区规则决定的是"哪些数据放在一起",例如:
- HASH 分区按照分区键的哈希值分散数据。
- RANGE 分区按照键值范围组织数据。
分区规则不直接指定数据放到哪个 Zone。分区在 OBServer 上的分布,以及副本的放置和数量,由 OceanBase 结合租户资源、Zone 布局、复制策略和调度机制进行管理。
六、选型建议
| 主要需求 | 优先考虑 |
|---|---|
| 按订单 ID 点查,关注数据和访问压力的均匀分布 | HASH 分区 |
| 按月份查询、归档、删除历史数据 | RANGE 分区 |
| 同时强依赖订单 ID 点查和时间范围查询 | 结合实际访问比例评估分区键、索引和二级分区方案 |
正式建表前,建议确认以下信息:
- OceanBase 的具体版本和 MySQL 兼容模式限制。
- 最主要的查询条件,以及是否需要按时间批量归档或删除。
- 预计数据量、写入峰值和数据增长速度。
- 是否要求
id全表唯一,以及是否需要全局唯一索引。 - 租户的 Zone 数量、资源规格和副本布局要求。