首批通过分布式安全可靠测评,为关键业务系统打造
函数分区键路由概述
更新时间:2026-04-14 17:41:09
本文主要介绍函数分区键的支持情况以及如何使用和验证是否路由成功。
背景
OceanBase 数据库是分布式数据库, 每个表甚至每个表的不同分区都可能存放在不同的机器上,若要对表进行读写,就必须先要定位到数据所属的表或是分区的位置。尽管任何一台 OBServer 节点均拥有路由转发和本地执行的功能,但实际场景中,SQL 请求没有转发到最恰当的 OBServer 节点将严重影响性能和整体吞吐量。
ODP 作为 OceanBase 数据库的高性能反向代理服务器,其核心功能就是路由转发,因此若 ODP 支持更多的函数解析,就可以实现将具体 SQL 转发的最恰当的 OBServer 节点上执行,从而减少慢查询的出现次数,提升用户体验。
使用说明
函数分区键路由为 ODP V4.2.3 迭代功能,使用该功能时需确保您所使用的 ODP 为 V4.2.3 或之后版本。
函数不支持的场景仅代表 ODP 无法准确路由,会随机发给 OBServer 节点,但是这些函数的功能本身 OceanBase 数据库是支持的。
ODP 计算分区键的目的在于准确路由用户的请求,ODP 分区路由的错误不影响请求执行的正确性。
ODP 的 SQL 解析器只能针对 SQL 做简单的解析,在部分场景下(比如嵌套查询),分区键计算可能会有不准确的情况,推荐使用案例中的用法,详细用法可参见本章后续文档中介绍。您也可根据下文介绍的 验证思路 验证当前 SQL 是否能准确计算。
使用示例
本节仅简单给出不同场景下的使用示例,详细的各个函数的使用示例可参见本章后续文档介绍。
分区键场景
创建表
test1create table test1 ( c1 int, c2 int ) partition by hash(c1) partitions 3;查看
test1表中c1列为1的数据select * from test1 where c1 = nvl(null, 1);
默认值场景
创建表
test2,并设置c1列默认值为nvl(null,12.3)create table test2 ( c1 int default nvl(null,12.3), c2 int ) partition by hash(c1) partitions 3;向
test2表中插入数据因
c1列存在默认值,此时仅需插入c2列的值。insert into test2 (c2) values (2);
生成列场景
创建表 test3,并设置 c2 列值由表达式 substr(c1, 1, 4) 自动生成。
create table test3(
c1 varchar2(20) default 'aaaaaaaaaaaa',
c2 varchar2(20) GENERATED ALWAYS AS (substr(c1, 1, 4))
) partition by hash(c2) partitions 3;
支持情况
ODP 函数分区键路由支持分区键、默认值以及生成列,下文将分别为您介绍。
分区键和默认值
本节介绍分区键和默认值中支持的函数,ODP 支持基本的四则运算(包括 +、-、*、/),详细使用可参加下文 基本函数用法。除此之外还支持字符串函数、数字计算函数和日期计算函数。本节将分别介绍 ODP 对各函数的支持情况。
字符串函数
ODP 对字符串函数的支持情况如下表,具体的用法可参见 字符串函数。
| 函数 | 功能 | 支持模式 |
|---|---|---|
| SUBSTR | 从字符串中提取子串 | MySQL/Oracle |
| TO_CHAR(datetime) | 将 datetime 类型数据转为 char 类型 | Oracle |
| LOWER | 将字符转为小写格式 | MySQL/Oracle |
| LCASE | 将字符转为小写格式 | MySQL |
| UPPER | 将字符转为大写格式 | MySQL/Oracle |
| UCASE | 将字符转为大写格式 | MySQL |
| TRIM | 移除字符串两侧(或指定一侧)的指定字符 | MySQL/Oracle |
| LTRIM | 移除字符串左侧的指定字符 | MySQL/Oracle |
| RTRIM | 移除字符串右侧的指定字符 | MySQL/Oracle |
| CONCAT | 将一个或多个字符串连接成一个新的单一字符串 | MySQL/Oracle |
| SUBSTRING | 从字符串中提取子串 | MySQL |
| REPLACE | 将字符串中的某个子串替换为另一个子串 | MySQL/Oracle |
| LENGTH | 计算字符串的长度 | MySQL/Oracle |
数字计算操作
ODP 对数字计算操作相关函数的支持情况如下表,具体的用法可参见 数字计算操作。
| 函数 | 说明 | 支持模式 |
|---|---|---|
| MOD | 返回两个数相除后的余数 | Oracle |
| NVL | 用于处理 NULL 值 | Oracle |
| ISNULL | 返回值为 1/0 | MySQL |
| ABS | 返回其参数的绝对值 | MySQL/Oracle |
| CEIL/CEILING | 返回大于或等于其参数的最小整数 | MySQL/Oracle |
| FLOOR | 返回小于或等于其参数的最大整数 | MySQL/Oracle |
| TRUNCATE(MySQL) TRUNC(Oracle) |
对数字进行截断处理(不进行四舍五入) | MySQL/Oracle |
| ROUND | 对数值进行四舍五 | MySQL/Oracle |
日期计算操作
ODP 对日期计算操作相关函数的支持情况如下表,具体的用法可参见 日期计算操作。
| 函数 | 说明 | 支持模式 |
|---|---|---|
| CURDATE/CURRENT_DATE | 当前时区日期,不含时间部分 | MySQL |
| CURTIME/CURRENT_TIME | 当前时区时间,不含日期部分 | MySQL |
| CURRENT_TIMESTAMP/NOW | 当前时区的日期时间 | MySQL |
| CURRENT_DATE | 当前会话时区中的当前日期 | Oracle |
| CURRENT_TIMESTAMP | 当前会话时区的当前日期 | Oracle |
| SYSTIMESTAMP | 数据库服务器操作系统日期(timestamp) | Oracle |
| SYSDATE | 数据库服务操作系统日期(date) | Oracle |
| TO_DATE | 将字符串转换为日期或日期时间类型数据 | Oracle |
| TO_TIMESTAMP | 将字符串转换为 timestamp 类型的日期时间数据 | Oracle |
生成列
目前仅支持 SUBSTR 函数,且有如下两条注意事项:
生成列以及对应的源列的类型需要是字符串类型,比如 varchar、text 等。
生成列的 SUBSTR 函数计算暂时不支持使用 multi-char 字符做计算。
基本函数用法
目前 ODP 支持在分区键以及默认值中使用四则运算嵌套函数,在使用功能带有函数的四则运算时,您需注意使用函数的支持情况。
使用示例如下:
分区键
此处假设数据库中存在表
test1,c1列为分区键,查看test1表中符合条件的数据,ODP 可以将该 SQL 请求准确路由。select * from test1 where c1 = ((((-(2)*(2-4)) +mod(mod((1+(3*2)),2),1)) -(4/2))+ mod(1,2));默认值
创建表
test2,并通过函数定义列c1。CREATE TABLE test2( c1 number default mod(5,2)+1, c2 int ) PARTITION BY HASH (c1) PATITIONS 3;
验证思路
本节介绍如何验证当前 SQL 是否能准确计算。
您可通过 ODP V4.2.2 或之后版本中的路由诊断功能判断 ODP 是否正确路由。执行 explain route <your_sql>; 命令查看输出的 Route Prompts 中是否含有 WARN,没有 WARN 代表分区路由准确。详细的路由诊断功能可参见 路由诊断。
以出现不支持的情况为例,错误路由的输出如下:
Trans Current Query:"explain route select * from t1 where c1=sin(1)"
Route Prompts
-----------------
> ROUTE_INFO
[INFO] Will route to partition server or ldc
> RESOLVE_TOKEN
[WARN] Not support expr func(OB_PROXY_EXPR_TYPE_NONE)
Route Plan
-----------------
> SQL_PARSE:{cmd:"COM_QUERY", table:"t1"}
> ROUTE_INFO:{route_info_type:"USE_PARTITION_LOCATION_LOOKUP"}
> LOCATION_CACHE_LOOKUP:{mode:"oceanbase"}
> TABLE_ENTRY_LOOKUP_DONE:{table:"t1", table_id:500010, partition_num:3, table_type:"USER TABLE", entry_from_remote:false}
> PARTITION_ID_CALC_START:{}
> EXPR_PARSE:{col_val:"c1=sin"}
> RESOLVE_EXPR:{error:-5055, part_range:"(MIN ; MAX)always true"}
> RESOLVE_TOKEN:{error:-5055, token_type:"TOKEN_FUNC", token:"sin"}
> PARTITION_ID_CALC_DONE:{error:-5055, partition_id:-1, level:1, partitions:"(p-1)"}
> ROUTE_INFO:{route_info_type:"USE_CACHED_SESSION", svr_addr:"10.10.10.1:21102"}
> CONGESTION_CONTROL:{svr_addr:"10.10.10.1:21102"}
正确路由的输出如下:
Trans Current Query:"explain route select * from t1 where c1=mod(5,2)"
Route Prompts
-----------------
> ROUTE_INFO
[INFO] Will route to partition server or ldc
Route Plan
-----------------
> SQL_PARSE:{cmd:"COM_QUERY", table:"t1"}
> ROUTE_INFO:{route_info_type:"USE_PARTITION_LOCATION_LOOKUP"}
> LOCATION_CACHE_LOOKUP:{mode:"oceanbase"}
> TABLE_ENTRY_LOOKUP_START:{}
> FETCH_TABLE_RELATED_DATA:{part_level:1, part_expr:"c1"}
> TABLE_ENTRY_LOOKUP_DONE:{table:"t1", table_id:500010, partition_num:3, table_type:"USER TABLE"}
> PARTITION_ID_CALC_START:{}
> EXPR_PARSE:{col_val:"c1=mod"}
> RESOLVE_EXPR:{part_range:"[1 ; 1]"}
> RESOLVE_TOKEN:{token_type:"TOKEN_FUNC", resolve:"DECIMAL:1", token:"mod", expr_type:"OB_PROXY_EXPR_TYPE_FUNC_MOD"}
> CALC_PARTITION_ID:{part_description:"partition by hash(INT<binary>) partitions 3"}
> PARTITION_ID_CALC_DONE:{partition_id:200003, level:1, partitions:"(p1)"}
> PARTITION_ENTRY_LOOKUP_DONE:{leader:"10.10.10.1:21100"}
> ROUTE_POLICY:{chosen_route_type:"ROUTE_TYPE_LEADER"}
> CONGESTION_CONTROL:{svr_addr:"10.10.10.1:21100"}