首批通过分布式安全可靠测评,为关键业务系统打造
基于 OceanBase 分布式分区分裂能力提升大表查询性能
更新时间:2026-05-19 13:03:54
以 MySQL 数据库为例,为您介绍使用分布式分区能力提升大表查询性能的使用方法。
概念介绍
分区分裂
在 OceanBase 数据库中,分区是指根据一定的规则,把一个表分解成多个更小的、更容易管理的部分。每个分区都是一个独立的对象,具有自己的名称和可选的存储特性。分区分裂是数据库中将一个分区按照一定规则分成两个或多个新分区的过程。这一过程通常发生在数据库的大表中,以避免单个分区数据量过大,从而实现更好的数据管理和查询性能。 更多内容参见分区分裂概述。
手动分区分裂
OceanBase 数据库支持在分区表中手动进行分区分裂操作,即将一个已有的分区拆分为多个分区。这个功能可以指定需要分裂的分区和新分区的分裂位点进行手动执行分区分裂命令,根据需求和数据增长情况对分区进行调整。当前版本仅支持对 Range/Range Columns 分区的一级分区表进行手动分区分裂操作,且只支持一个分区分裂成多个分区。 更多内容参见手动分区分裂。
Range/Range Columns
Range 分区根据分区表定义时为每个分区建立的分区键值范围,将数据映射到相应的分区中。它是常见的分区类型,经常跟日期类型一起使用。例如:可以将业务日志表按日/周/月分区。Range 分区的分区键必须是整数类型或 YEAR 类型,如果对其他类型的日期字段分区,则需要使用函数进行转换。Range 分区的分区键仅支持一列。如果要支持多列的分区键,或者其他数据类型,可以使用 Range Columns 分区。
Range Columns 分区的分区键支持数据类型,具体类型如下:
数值类型:TINYINT(BOOL/BOOLEAN)、SMALLINT、MEDIUMINT、INT(INTEGER)、BIGINT、DECIMAL(DECIMAL/DECIMAL[(M[,D])]/DEC/NUMERIC/FIXED)、FLOAT(FLOAT[(M,D)]/FLOAT(p))、DOUBLE(DOUBLE/DOUBLE[(M,D)]/DOUBLE PRECISION/REAL)。
时间日期类型:DATE、DATETIME、TIME、YEAR、TIMESTAMP。
字符型:CHAR、NCHAR、VARCHAR、NVARCHAR。 二进制类型:BINARY、VARBINARY。
Range Columns 分区列可以写多个列(即列向量)。
Range Columns 分区列不要求是整型,可以是任意类型。
Range Columns 分区定义不支持表达式。
对于 Range/Range Columns 分区,只能在最大的分区之后添加一个分区,不可以在中间或者开始的地方添加。如果当前的分区中有 MAXVALUE 的分区,则不能继续添加分区。
向 Range/Range Columns/List/List Columns 分区中添加一级分区不会影响全局索引和局部索引的使用。
更多内容参见分区类型。
Hash
Hash 分区适合于对不能用 Range 分区、List 分区方法的场景,它的实现方法简单,通过对分区键上的 Hash 函数值来散列记录到不同分区中。如果您的数据符合下列特点,使用 Hash 分区是个很好的选择:
不能指定数据的分区键的列表特征。
不同范围内的数据大小相差非常大,并且很难手动调整均衡。
使用 Range 分区后数据聚集严重。
并行 DML、分区剪枝和分区连接等性能非常重要。
更多内容参见分区类型。
主键
主键值规则(Primary Key Value Rule)是定义在某一键 Key(键指一列或一个列集)上的规则,其作用是确保表内的每一数据行都可以由某一个键值唯一地确定。 更多内容参见主键约束。
场景介绍
一张电商系统的优惠券表,每次客户下单时都会查询该客户拥有的优惠券(通过 user_id 查询)。经过几年的业务发展,该表的数据量来到了 5 个亿,虽然当前的 MySQL 库对 user_id 建了索引,但随着数据量的增长查询逐步衰退,且逐步接近 MySQL 单机上限。 这时候就可以通过 OceanBase 的分区能力来优化性能,对优惠券表使用user_id字段进行 HASH 分区,将该表拆分成 16个分区,这样单个分区平均只有 3000 多万行数据,对于优惠券查询有极大性能提升。此外,在零售系统常见的大促和活动场景,还可以使用到 OceanBase 数据库的分布式扩展能力,增加节点以自动负载均衡。
前提条件
您拥有当前实例的实例管理员、数据读取和数据服务管理员权限,如无权限,可联系组织管理员进行添加。
您的环境中有可用的事务型(MySQL)集群实例。请参考 创建租户完成租户创建后,并创建数据库和账号。
分区设计
对于优惠券表:
- 使用 RANGE 分区:按优惠券发行时间(create_time)将数据分区,便于分离历史数据。
- 使用 HASH 分区:在每个时间范围内,根据 user_id 的 HASH 值进一步拆分子分区,均匀分布查询压力。 通过这种分区设计单个分区数据量仅约 3000 万行,查询性能显著提升,历史数据按时间范围便于清理和管理。
操作步骤
进入 SQL 控制台页面。
选择您创建的账号并填入密码登录,单击 确认。
双击左侧数据库如
default_database,打开一个新的 SQL 窗口。创建一张电商系统的优惠券表并创建分区。
CREATE TABLE coupon ( coupon_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '优惠券ID(主键)', user_id BIGINT NOT NULL COMMENT '用户ID,用于查询用户的优惠券', coupon_code VARCHAR(64) NOT NULL COMMENT '优惠券代码,唯一标识', status TINYINT NOT NULL DEFAULT 1 COMMENT '优惠券状态:1=未使用,2=已使用,3=已过期', create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', expire_time TIMESTAMP NOT NULL COMMENT '到期时间', PRIMARY KEY (user_id, create_time, coupon_id), -- 将 create_time 加入主键 KEY idx_coupon_user_id (user_id) ) PARTITION BY RANGE COLUMNS (create_time) -- 使用 RANGE COLUMNS 按 create_time 分区 SUBPARTITION BY HASH (user_id) -- 按 user_id HASH 分区 SUBPARTITIONS 8 -- 每个分区拆分为 8 个子分区 ( PARTITION p2021_q1 VALUES LESS THAN ('2021-04-01'), PARTITION p2021_q2 VALUES LESS THAN ('2021-07-01'), PARTITION p2021_q3 VALUES LESS THAN ('2021-10-01'), PARTITION p2021_q4 VALUES LESS THAN ('2022-01-01'), PARTITION p2022_q1 VALUES LESS THAN ('2022-04-01'), PARTITION p2022_q2 VALUES LESS THAN ('2022-07-01'), PARTITION p2023_q1 VALUES LESS THAN ('2023-04-01'), PARTITION p_future VALUES LESS THAN MAXVALUE );其中:
RANGE 分区:数据按 create_time 的时间戳范围进行分区,例如:半年一个分区。历史数据可以按时间范围归档,同时避免全表扫描。
HASH 分区:每个时间分区内的数据再按
user_id进行 HASH 子分区。子分区数设为 16,将原表拆分为 6 个时期分区 × 16 子分区 = 96 个最终分区。
数据插入数据,OceanBase 数据库会依据
create_time和user_id自动将数据写入适当的 RANGE 分区和 HASH 子分区。INSERT INTO coupon (user_id, coupon_code, status, create_time, expire_time) VALUES (1001, 'DISCOUNT2023', 1, '2023-03-15 08:00:00', '2023-12-31 23:59:59'), (1002, 'DISCOUNT2023', 1, '2022-06-01 12:30:00', '2022-12-31 23:59:59'), (1003, 'DISCOUNT2023', 1, '2021-05-03 10:15:00', '2021-12-31 23:59:59'), (1004, 'DISCOUNT2024', 1, '2024-01-01 06:00:00', '2024-03-31 23:59:59');查询优化示例:查询单个用户的优惠券,OceanBase 数据库会优先利用分区规则,通过
create_time定位到指定的 RANGE 分区,再按user_id到对应的 HASH 子分区查询。查询某用户在指定时间范围内的优惠券:
SELECT * FROM coupon WHERE user_id = 1001 AND create_time >= '2023-01-01' AND create_time < '2023-07-01';
按时间范围查询所有用户的优惠券:
SELECT * FROM coupon WHERE create_time >= '2022-01-01' AND create_time < '2023-01-01';
查询未使用的优惠券,结合分区规则和 status 字段筛选:
SELECT * FROM coupon WHERE status = 1 AND create_time >= '2023-01-01' AND create_time < '2023-07-01';