首批通过分布式安全可靠测评,为关键业务系统打造
AP 场景下的表分区设计实践
更新时间:2026-06-20 12:56:43
分析型业务通常需要对海量数据进行分析计算,这对数据库的查询能力以及数据管理能力都有很高的要求。OceanBase 通过分区技术,将一张表的数据按照分区键水平拆分成多个数据子集,有助于查询效率提升和数据管理能力:
- 查询效率提升: 分区裁剪能减少无关数据的扫描。
- 数据维护: 支持按照分区粒度进行数据管理,比如数据归档、清理等。
- 数据分布: 按照分区粒度进行数据分布,能够将数据打散到多个节点上,具备良好的可扩展性。
分区的作用
在 OceanBase 中,分区是水平分片的基本单位,是数据分布、负载均衡和并行操作的最小物理单元。一张大表被逻辑地分割成多个更小、更易管理的独立块,每个分区(甚至分区的不同副本)都可以分散存储在集群中不同的 OBServer 节点上。
这种设计为分析型业务带来了根本性优势:当单个节点的存储或计算能力达到瓶颈时,只需通过增加节点并重新分布分区,即可实现近乎线性的水平扩展,轻松应对 PB 级数据规模。
分区裁剪提升查询效率
使用分区后,指定分区列进行查询时,在某些场景下能够裁剪出满足查询条件的分区,使得查询无须查询那些不满足条件的分区。
参考如下示例,在列 c2 上创建 hash 分区,指定 c2 = 1 的查询条件,能够裁剪出只需要查询分区 p1。
-- 创建一张四个 hash 分区的表格 t1,分区键为 C2
CREATE TABLE t1(c1 INT, c2 INT) PARTITION BY HASH(c2) PARTITIONS 4;
-- 指定 c2 = 1 查询,裁剪出分区 p1
EXPLAIN SELECT * FROM t1 WHERE c2 = 1;
执行计划输出显示:
+------------------------------------------------------------------------------------+
| Query Plan |
+------------------------------------------------------------------------------------+
| =============================================== |
| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| |
| ----------------------------------------------- |
| |0 |TABLE FULL SCAN|t1 |1 |3 | |
| =============================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([t1.c1], [t1.c2]), filter([t1.c2 = 1]), rowset=16 |
| access([t1.c2], [t1.c1]), partitions(p1) |
| is_index_back=false, is_global_index=false, filter_before_indexback[false], |
| range_key([t1.__pk_increment]), range(MIN ; MAX)always true |
+------------------------------------------------------------------------------------+
分区裁剪可以过滤掉不需要的数据,但分区数太多可能也会导致其他的问题,例如元数据量过多、分区裁剪的效率可能降低等等。因此,在 OceanBase 的列存表中建议单个分区的行数 ≥ 100 W 行。
分区作为数据维护单元
在数据库运维中,将分区作为基本的数据维护单元,能较大地简化日常管理流程,例如数据清理场景、分区级收集统计信息等。
以数据清理场景为例,当数据按时间进行分区后,清理过期数据就不再需要逐行删除,而是直接通过删除整个历史分区来实现。这种操作仅仅只需要修改元数据,还能彻底释放磁盘空间,避免了传统的 DML 删除操作产生的性能开销。
通过分区键(如时间)将数据自然归类,使维护操作从 “逐行扫描” 变为 “批量处理”,极大地提升了管理效率,降低了运维的复杂度。
分区作为数据分布单元
分区作为 OceanBase 的数据分布单元,每个分区的副本可以放置在不同的 OBServer 节点,以实现存储和计算的扩展。
存储的扩展: 当创建一个分区表时,这些分区及其副本可以根据集群的资源情况,由 OceanBase 自动调度到不同的物理节点上。这意味着单张表的容量不再受限于单机磁盘,而是整个集群的存储容量。当集群的存储空间不足时,通过加节点就能够实现扩展。
计算的并行化: 这是分析型业务实现高性能的关键因素之一。当一个查询(特别是涉及全表扫描或大规模聚合的查询)被执行时,OceanBase 的优化器会识别出查询涉及的分区。查询任务可以被分解成多个子任务,并下推到各个数据分区所在的节点上并行执行。例如,一个
SUM()操作会在每个分区本地先计算小计,然后将中间结果汇总得到最终总和,这充分利用了多节点的计算能力,从而显著加速查询。
OceanBase 的基础分区方式
目前 OceanBase 中支持三大类基础的分区方式,包括 Hash/Key、Range/Range Columns 和 List/List Columns。三种分区方式各自的使用场景有所不同。
HASH/KEY 分区
一般适用于分区列 NDV(不同值的种类)较大,且难以划分出明确范围的情况。优点是容易让没有特定规则的数据也能够在不同的分区内均匀分布,缺点是在范围查询时难以进行分区裁剪。
适用场景举例:
- 无明显查询模式,需均匀分布数据到多个节点(如用户 ID、交易 ID)。
设计要点:
分区键选择:
- NDV(唯一值数量)远大于分区数(如用户 ID 的 NDV 应远大于分区数)。
- 优先选择无倾斜(或只有少量倾斜)的整型 / 时间列(如
user_id,order_time,或者自增列)。 - 高频查询条件字段(如
user_id作为 Join 关键字)。
分区数推荐:
- 确保分区数匹配集群的机器数量,避免资源分配不均衡
示例场景:
-- Hash 分区,按 user_id 均匀分布
CREATE TABLE customer (
user_id BIGINT NOT NULL,
login_time TIMESTAMP NOT NULL,
customer_name VARCHAR(100) NOT NULL,
phone_num BIGINT NOT NULL,
city_name VARCHAR(50) NOT NULL,
sex INT NOT NULL,
id_number VARCHAR(18) NOT NULL,
home_address VARCHAR(255) NOT NULL,
office_address VARCHAR(255) NOT NULL,
age INT NOT NULL
)
PARTITION BY HASH(user_id) PARTITIONS 128;
Range/Range Columns 分区
一般适用于分区键容易划分出明确的范围的情况,例如可以把记录流水信息的大表,根据表示信息时间的列做 RANGE 分区。
适用场景举例:
- 数据按时间 / 数值范围增长(如
order_time,price)。 - 需快速裁剪历史数据(如仅查询最近一个月数据)。
设计要点:
分区键选择:
- 时间字段(如
order_time)或连续数值字段。 - 分区边界需与业务查询条件对齐(如按天 / 月划分)。
- 时间字段(如
分区数推荐:
- 根据数据增长设置分区,例如按照月份分区。
示例场景:
-- 创建系统日志表,按日志时间进行月度 RANGE 分区,支持快速查询与数据归档
CREATE TABLE system_logs (
log_id BIGINT,
log_date TIMESTAMP NOT NULL,
log_level VARCHAR(10),
source_system VARCHAR(50),
user_id BIGINT,
log_message TEXT,
client_ip VARCHAR(15)
)
-- 主分区:按月 RANGE 分区,使用日期直接表达分区边界
PARTITION BY RANGE COLUMNS(log_date) (
PARTITION p_202001 VALUES LESS THAN ('2020-02-01'),
PARTITION p_202002 VALUES LESS THAN ('2020-03-01'),
PARTITION p_202003 VALUES LESS THAN ('2020-04-01'),
PARTITION p_202004 VALUES LESS THAN ('2020-05-01'),
PARTITION p_202005 VALUES LESS THAN ('2020-06-01'),
PARTITION p_202006 VALUES LESS THAN ('2020-07-01'),
PARTITION p_202007 VALUES LESS THAN ('2020-08-01'),
PARTITION p_202008 VALUES LESS THAN ('2020-09-01'),
PARTITION p_202009 VALUES LESS THAN ('2020-10-01'),
PARTITION p_202010 VALUES LESS THAN ('2020-11-01'),
PARTITION p_202011 VALUES LESS THAN ('2020-12-01'),
PARTITION p_202012 VALUES LESS THAN ('2021-01-01'),
-- 默认分区处理未来数据或时间格式异常的记录
PARTITION p_future VALUES LESS THAN (MAXVALUE)
);
List/List Columns 分区
一般适用于需要显式控制各行数据如何映射到具体的某一个分区时,优点是可以对无序或无关的数据集进行精准分区,缺点是在范围查询时难以进行分区裁剪。
适用场景举例:
- 离散型字段(如地区、渠道类型)。
- 需按固定类别快速裁剪数据(如查询华东地区用户)。
设计要点:
分区键选择:
- 离散值且数量有限(如
region字段仅有['east','west','south','north'])。 - 分区值需覆盖所有可能取值,避免遗漏。
- 离散值且数量有限(如
分区数限制:
- 根据业务逻辑进行配置分区数。
示例场景:
CREATE TABLE orders_by_region (
order_id BIGINT COMMENT '订单唯一标识',
region_code INT NOT NULL PRIMARY KEY COMMENT '区域代码(1=north/china, 2=east/china, 3=south/china, 4=west/china)',
customer_id BIGINT COMMENT '客户 ID',
order_time DATETIME COMMENT '订单创建时间',
product_category VARCHAR(50) COMMENT '商品类别',
order_amount DECIMAL(18,2) COMMENT '订单金额',
payment_status VARCHAR(20) COMMENT '支付状态(如:PAID, UNPAID)'
)
PARTITION BY LIST(region_code) -- 改为整数类型分区键
(
PARTITION p_north VALUES IN (1), -- 区域代码 1 对应 north/china
PARTITION p_east VALUES IN (2),
PARTITION p_south VALUES IN (3),
PARTITION p_west VALUES IN (4),
PARTITION p_other VALUES IN (DEFAULT) -- 默认分区处理未知区域
);
灵活的分区管理能力
OceanBase 有非常灵活的分区管理能力。从数据管理的角度来看,它既有数据维护的功能,也有数据分布的功能;从使用方式来讲,它有手动管理和自动管理两种方式;从分区的层次来考虑,它支持一级分区和二级分区组合使用。通过不同的组合,满足用户对于数据管理的不同需求。
本节将从 数据维护 和 数据分布 两个角度展开,同时在两个角度中考虑使用方式以及分区层次的能力组合。
数据维护
业务层通常按照时间维度来管理分区,方便做数据的归档、清理等操作。我们从业务的完整数据生命周期流程来结合描述手动分区管理能力:
- 业务建表: 创建按照时间分区的表格,提前创建未来一段时间需要的分区。
- 业务导数: 导入数据。
- 业务运行: 随着时间的推进,可能提前创建的分区不足,继续提前创建未来一段时间需要的分区。
- 定期数据清理: 当数据积累到一定时间后,可能之前的数据就不需要了,此时可以删除不需要的分区。
示例:
-- 1. 创建分区表(按天分区,预创建未来 7 天分区)
CREATE TABLE business_data (
id BIGINT NOT NULL AUTO_INCREMENT,
event_time DATETIME NOT NULL,
metric_value DECIMAL(10,2),
PRIMARY KEY (id, event_time)
) PARTITION BY RANGE COLUMNS(event_time) (
PARTITION p20231025 VALUES LESS THAN ('2023-10-26'),
PARTITION p20231026 VALUES LESS THAN ('2023-10-27'),
PARTITION p20231027 VALUES LESS THAN ('2023-10-28'),
PARTITION p20231028 VALUES LESS THAN ('2023-10-29'),
PARTITION p20231029 VALUES LESS THAN ('2023-10-30'),
PARTITION p20231030 VALUES LESS THAN ('2023-10-31'),
PARTITION p20231031 VALUES LESS THAN ('2023-11-01') -- 预创建未来 7 天分区
);
-- 2. 导入数据,这里略过
-- 3. 预创建未来 7 天分区
ALTER TABLE business_data ADD PARTITION (
PARTITION p20231101 VALUES LESS THAN ('2023-11-02'),
PARTITION p20231102 VALUES LESS THAN ('2023-11-03'),
PARTITION p20231103 VALUES LESS THAN ('2023-11-04'),
PARTITION p20231104 VALUES LESS THAN ('2023-11-05'),
PARTITION p20231105 VALUES LESS THAN ('2023-11-06'),
PARTITION p20231106 VALUES LESS THAN ('2023-11-07'),
PARTITION p20231107 VALUES LESS THAN ('2023-11-08')
);
-- 4. 定期数据清理,例如数据到期后,删除 7 天的数据
ALTER TABLE business_data DROP PARTITION p20231025, p20231026, p20231027, p20231028, p20231029, p20231030, p20231031;
由于数据在不停地写入,手工维护预创建分区和定期清理分区还是比较麻烦的。为了简化这个流程,OceanBase 提供了 动态分区功能,支持按固定时间分区、预创建多长时间的分区和保留多久的历史分区等功能。
对于上面的例子,假如我们需要保留 30 天数据,每次预创建 7 天的分区,那么使用如下语法进行创建:
-- 1. 创建分区表,设置动态分区策略
CREATE TABLE t1 (
id BIGINT NOT NULL AUTO_INCREMENT,
event_time DATETIME NOT NULL,
metric_value DECIMAL(10,2),
PRIMARY KEY (id, event_time))
DYNAMIC_PARTITION_POLICY
(
ENABLE = true,
TIME_UNIT = 'day',
PRECREATE_TIME = '7day',
EXPIRE_TIME = '30day'
)
PARTITION BY RANGE COLUMNS (event_time)(
PARTITION p20231025 VALUES LESS THAN ('2023-10-26'));
除了 Range 分区模式外,业务也可以按照业务需求选择其他基础分区方式。
更多关于动态分区的信息,可参见动态分区、MySQL 模式下创建动态分区表 和 Oracle 模式下创建动态分区表
数据分布
分区也可以作为数据分布管理的单元。通常情况下为了数据打散,一般使用 HASH 分区的方式,它有如下优势:
- 它通常能有效实现数据的均匀分布,并支持基于分区键的精确分区裁剪。
- 对于需要进行 JOIN 操作的多张表,若均以相同的 JOIN 键进行 HASH 分区,且分区数量一致,再结合 OceanBase 的 Table Group 功能,可将各表中哈希值相同的分区绑定到同一组节点上,从而在执行 JOIN 时启用 Partition-Wise Join,避免跨节点的数据 Shuffle,显著提升查询性能。
Hash 分区也存在限制:
- Hash 分区的分区数设置之后,修改分区数是一个比较重的操作,涉及到整张表的数据重写,所以一般设置好 Hash 分区的分区数之后,就一般不再变化,比较难以实现可扩展
- 对于分区键上的范围查询,无法裁剪出分区,需要访问所有的分区,可能会存在读放大
为了解决 HASH 分区可扩展性以及范围查询的问题,OceanBase 已经支持行存表的自动分区分裂能力。
混合数据维护和数据分布管理
我们也可以以二级分区的方式,同时支持数据维护和数据分布的需求。通常使用比较多的场景为:一级分区用于数据维护的需求,二级分区用于数据分布的需求,每种需求可以使用对应需求所支持的方式进行组合。
典型的手动分区管理方式
- 一级分区:
- 类型选择:使用 Range 或者 List 分区,匹配高频查询条件(如时间范围、地区)
- 分区数建议:根据查询条件时间分布、数据维护的需求设置合理范围(如按月分区保留 12 个月,或按地区分为 4 个 List 分区)
- 二级分区:
- 类型选择:使用 Hash 分区,保证数据打散
- 分区数推荐:
- 如果只有一个一级分区写入,那么一级分区的二级分区数需要满足写入打散的资源诉求
- 如果有多个一级分区能够写入,那么能写入的一级分区数 × 二级分区数满足写入打散的资源诉求即可
以下是 Range + Hash 和 List + Hash 的两个场景案例:
Range + Hash
一级选择 Range 分区,指定 order_date 后,可以快速过滤掉不需要扫描数据的分区,也能够通过分区管理操作快速进行数据维护;二级选择 Hash 分区,可以将当月的写入或者读取打散到 8 个分区中,避免热点。
CREATE TABLE orders (
user_id BIGINT NOT NULL COMMENT '用户 ID(二级分区键)',
order_date DATE NOT NULL COMMENT '下单日期(一级分区键)',
amount DECIMAL(10,2) NOT NULL COMMENT '订单金额',
status TINYINT NOT NULL COMMENT '状态: 0-取消 1-待支付 2-已支付 3-已发货 4-已完成',
region_code CHAR(6) NOT NULL COMMENT '地区编码(前 2 位省码)',
product_id INT NOT NULL COMMENT '商品 ID',
payment_method VARCHAR(20) COMMENT '支付方式',
created_at TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP(6) COMMENT '记录创建时间')
PARTITION BY RANGE COLUMNS(order_date)
SUBPARTITION BY HASH(user_id) SUBPARTITIONS 8
(
PARTITION p202501 VALUES LESS THAN ('2025-02-01'),
PARTITION p202502 VALUES LESS THAN ('2025-03-01'),
...
PARTITION p202601 VALUES LESS THAN ('2026-02-01')
);
List + Hash
一级选择 List 分区,指定省份能够裁剪到相应的分区,也可以按照省维度进行数据维护;二级选择 Hash / Key 分区,可以将省的读写流量打散到多个分区中,实现负载均衡。
-- 一级分区:LIST 按省划分(31 个省级行政区)
CREATE TABLE social_insurance_records (
record_id BIGINT,
province_code INT NOT NULL, -- 省级编码(如 11 北京,31 上海)
payment_date DATE NOT NULL,
user_id VARCHAR(32) NOT NULL,
amount DECIMAL(10,2)
) PARTITION BY LIST (province_code) -- 一级 LIST 分区
SUBPARTITION BY KEY(user_id) SUBPARTITIONS 16 -- 二级 HASH 分区
(
PARTITION p_beijing VALUES IN (11),
PARTITION p_shanghai VALUES IN (31),
PARTITION p_tianjin VALUES IN (12),
...
PARTITION p_xizang VALUES IN (54)
);
典型的自动分区管理方式
- 一级分区: 选择动态分区,配置按固定时间分区,预创建多长时间的分区和保留多久的历史分区等参数
- 二级分区: 选择自动 Range 分区分裂,能够自动地进行分裂,无须配置分区个数或者分区规则