在 OceanBase 数据库 V4.3.5 版本之前 OBServer 内核仅支持整表数据导入走旁路导入路径,从而加速导入。如果用户只想导入部分分区的数据,可以通过分区交换的方式,即先通过全量旁路导入将分区的数据导入到非分区表中,然后将对应非分区表与目标分区做分区交换,这个操作对用户来说比较麻烦。为了更好地支持分区级的数据导入,从 OceanBase 数据库 V4.3.5 版本开始支持 load data 和 insert into select 语法指定分区走旁路导入路径。
本文主要介绍指定分区进行旁路导入的基本用法、功能限制和示例说明。
详细说明
全量旁路导入的底层原理是导入任务启动时新建一个隐藏表,往隐藏表内部导入数据。待导入任务结束之后,交换隐藏表和原表,完成旁路导入。这就意味着每一次全量旁路导入都会将原表数据重新全部写入到隐藏表之中。如果用户执行全量旁路导入时,只向一个或者若干个分区内部导入数据,虽然导入并不涉及其余分区,但因为旁路导入的实现,其余分区的数据也会被重写到隐藏表之中。很显然,对于不涉及分区的数据重写是没有必要的。指定分区旁路导入就可以解决该问题。指定分区旁路导入在任务最后,只会交换用户指定的分区,而不是交换整张表,进而避免了重写原表中的其余分区的数据。
下面会详细介绍一下指定分区进行旁路导入的基本语法、功能限制和示例。
测试版本
OceanBase 数据库 V4.3.5 Hotfix1 版本。
基本语法
指定分区旁路导入通过 partition 关键字指定相关的分区(包括一级分区和二级分区),对于 load data 和 insert into select,只需要在目标表后添加 partition 关键字并指定目标分区的名字即可开启指定分区旁路导入,具体语法如下所示。
load data使用指定分区旁路导入相关语法如下所示,partition 关键字用于指定分区。
-- load data 指定一级分区 load data /*+ direct(true,0) parallel(2) load_batch_size(100) */ infile "$FILE_PATH" into table t1 partition(p0, p1) fields terminated by '|' enclosed by '' lines starting by '' terminated by '\n';其中
p0、p1为指定导入的一级分区名称。-- load data 指定二级分区 load data /*+ direct(true,0) parallel(2) load_batch_size(100) */ infile "$FILE_PATH" into table t1 partition(p0sp0, p1sp1) fields terminated by '|' enclosed by '' lines starting by '' terminated by '\n';其中
p0sp0、p1sp1为指定导入的二级分区名称。insert into select使用指定分区旁路导入相关语法如下所示。-- insert into select 指定一级分区 insert /*+ enable_parallel_dml parallel(3) append */ into t2 partition(p0, p1) select * from t1 partition(p0, p1);-- insert into select 指定二级分区 insert /*+ enable_parallel_dml parallel(3) append */ into t2 partition(p0sp0, p1sp1) select * from t1 partition(p0sp0, p1sp1);
说明:上述例子虽然也是从 t1 的相同分区(p0,p1)或 t2 表的相同分区(p0sp0,p1sp1)导入数据,但是实际上 insert into select 并不关心数据来源,唯一需要确保的就是数据需要满足指定分区的分区要求,例如如下语句也是合法的。
# 假设 p0 分区规则为 partition p0 values less than (10)
# 假设 p1 分区规则为 partition p1 values less than (20)
# 假设 p2 分区规则为 partition p2 values less than (30)
# 假设 p3 分区规则为 partition p3 values less than (MAXVALUE)
# 以下语句均合法
insert /*+ enable_parallel_dml parallel(3) append */
into t2 partition(p0)
select * from t1 where t1.c1 < 10;
insert /*+ enable_parallel_dml parallel(3) append */
into t2 partition(p1)
select * from t1 where t1.c1 > 10 and t1.c1 < 20;
insert /*+ enable_parallel_dml parallel(3) append */
into t2 partition(p3)
select * from t1 where t1.c1 > 50;
# 假设 t1 的所有数据均落在 t2 的 p0 分区
insert /*+ enable_parallel_dml parallel(3) append */
into t2 partition(p0)
select * from t1;
功能限制
目标表不能含有自增列
auto_increment。目标表不能含有序列
sequence。目标表不能含有全局索引
global index。目标表不能是复制表。
如果目标表是一级分区表,那么最后分区交换的时候会交换相对应的分区。
如果目标表是二级分区表,如果指定一级分区导入,那么最后会交换该一级分区下所有的二级分区。
如果指定二级分区导入,那么最后只会交换该二级分区。
被交换分区只能是 range (columns)/list (columns) 分区,不能是 hash/key 分区。
示例如下:
-- Oracle 模式示例 obclient(SYS@oracle)[SYS]> create table tmp_table (id int, name varchar(100)); Query OK, 0 rows affected (0.182 sec) obclient(SYS@oracle)[SYS]> insert into tmp_table values (1,'a'),(2,'b'),(3,'c'); Query OK, 3 rows affected (0.026 sec) Records: 3 Duplicates: 0 Warnings: 0 obclient(SYS@oracle)[SYS]> commit; Query OK, 0 rows affected (0.001 sec) obclient(SYS@oracle)[SYS]> SELECT * INTO OUTFILE '/obbackup/obdumper_data/tmp_table.csv' FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' LINES TERMINATED BY '\n' FROM tmp_table; Query OK, 3 rows affected (0.006 sec) obclient(SYS@oracle)[SYS]> create table test_range_partition_table ( id integer, name varchar(100) ) partition by range(id) ( partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30), partition px values less than (maxvalue) ); Query OK, 0 rows affected (0.150 sec) obclient(SYS@oracle)[SYS]> load data /*+ direct(true,0) */ infile '/obbackup/obdumper_data/tmp_table.csv' into table test_range_partition_table partition(p1) fields terminated by ',' optionally enclosed by '"' lines terminated by '\n'; Query OK, 3 rows affected (1.170 sec) Records: 3 Deleted: 0 Skipped: 0 Warnings: 0 obclient(SYS@oracle)[SYS]> create table test_list_partition_table ( id integer, name varchar(100) ) partition by list(id) ( partition p1 values(1,2,3,4,5,7,8,9,10), partition p2 values(11,12,13,14,15,16,17,18,19,20), partition p3 values(default) ); Query OK, 0 rows affected (0.192 sec) obclient(SYS@oracle)[SYS]> load data /*+ direct(true,0) */ infile '/obbackup/obdumper_data/tmp_table.csv' into table test_list_partition_table partition(p1) fields terminated by ',' optionally enclosed by '"' lines terminated by '\n'; Query OK, 3 rows affected (1.049 sec) Records: 3 Deleted: 0 Skipped: 0 Warnings: 0-- MySQL 模式示例 obclient(root@mysql)[test]> create table tmp_table (id int, name varchar(100)); Query OK, 0 rows affected (0.489 sec) obclient(root@mysql)[test]> insert into tmp_table values (1,'a'),(2,'b'),(3,'c'); Query OK, 3 rows affected (0.046 sec) Records: 3 Duplicates: 0 Warnings: 0 obclient(root@mysql)[test]> commit; Query OK, 0 rows affected (0.000 sec) obclient(root@mysql)[test]> create table test_range_column_partition_table ( id integer, name varchar(100) ) partition by range columns (id) ( partition p1 values less than (10), partition p2 values less than (20), partition p3 values less than (30), partition px values less than (maxvalue) ); obclient(root@mysql)[test]> load data /*+ direct(true,0) */ infile '/obbackup/obdumper_data/tmp_table.csv' into table test_range_column_partition_table partition(p1) fields terminated by ',' optionally enclosed by '"' lines terminated by '\n'; Query OK, 3 rows affected (1.067 sec) Records: 3 Deleted: 0 Skipped: 0 Warnings: 0 obclient(root@mysql)[test]> create table test_list_column_partition_table ( id integer, name varchar(100) ) partition by list columns (id) ( partition p1 values in (1,2,3,4,5,7,8,9,10), partition p2 values in (11,12,13,14,15,16,17,18,19,20), partition p3 values in (default) ); obclient(root@mysql)[test]> load data /*+ direct(true,0) */ infile '/obbackup/obdumper_data/tmp_table.csv' into table test_list_column_partition_table partition(p1) fields terminated by ',' optionally enclosed by '"' lines terminated by '\n'; Query OK, 3 rows affected (1.053 sec) Records: 3 Deleted: 0 Skipped: 0 Warnings: 0对于最后一级分区为
range (columns)/list (columns)分区,指定一级或者二级分区都生效,但是如果二级分区是 hash/key 分区,或表是一级 hash/key 分区,指定分区导入将不生效。OBServer 日志中会报错[errcode=-4007] partition level direct load not support hash/key partitions(ret=-4007, ret="OB_NOT_SUPPORTED")。示例如下:
obclient(root@mysql)[test]> create table test_hash_partition_table (id integer, name varchar(100)) partition by hash (id) partitions 3; obclient(root@mysql)[test]> load data /*+ direct(true,0) */ infile '/obbackup/obdumper_data/tmp_table.csv' into table test_hash_partition_table partition(p1) fields terminated by ',' optionally enclosed by '"' lines terminated by '\n'; Query OK, 3 rows affected (1.053 sec) Records: 3 Deleted: 0 Skipped: 0 Warnings: 0 obclient(root@mysql)[test]> select last_trace_id() from dual; +-----------------------------------+ | last_trace_id() | +-----------------------------------+ | Yxxxx-xxxxx-0-0 | +-----------------------------------+ 1 row in set (0.001 sec)observer.log 相关日志如下:
[root@observer log]# grep Yxxxx-xxxxx-0-0 observer.log.2025012718* | grep ob_table_load observer.log.20250127181331525:[2025-01-27 18:09:32.021719] WDIAG [SERVER] check_support_direct_load_for_partition_level (ob_table_load_service.cpp:751) [381659][T1002_L0_G0][T1002][Yxxxx-xxxxx-0-0] [lt=32][errcode=-4007] partition level direct load not support hash/key partitions(ret=-4007, ret="OB_NOT_SUPPORTED") observer.log.20250127181331525:[2025-01-27 18:09:32.021747] WDIAG check_support_direct_load_for_partition_level (ob_table_load_service.cpp:752) [381659][T1002_L0_G0][T1002][Yxxxx-xxxxx-0-0] [lt=26][errcode=-4007] partition level direct-load not support hash/key partitions observer.log.20250127181331525:[2025-01-27 18:09:32.021755] WDIAG [SERVER] check_support_direct_load (ob_table_load_service.cpp:584) [381659][T1002_L0_G0][T1002][Yxxxx-xxxxx-0-0] [lt=7][errcode=-4007] fail to check support direct load for partition level(ret=-4007, ret="OB_NOT_SUPPORTED")
影响租户
影响 OceanBase 数据库中的 SYS 租户和 Oracle 租户以及 MySQL 租户。
适用版本
OceanBase 数据库 V4.3.5(oceanbase-4.3.5.0-100000122024123020)及之后版本。