基于湖库一体架构,统一管理结构化、半结构化与非结构化等多模态数据,一个系统承载事务处理、实时分析与 AI 工作负载。
租户内路由
更新时间:2026-07-22 16:51:01
租户内路由是指在获取租户的机器列表后,选择合适的节点执行 SQL。
对于租户内路由,ODP 可以像普通代理(如 HAProxy)一样,一个客户端连接对应一个服务端连接,服务端机器从租户机器列表选取,这样功能就会简单很多,但无法满足性能和高可用要求,可见路由影响了连接管理。
租户内路由是路由功能最复杂的部分,主要原因在于如何提供更好的性能和更高的可用性。本章将按照主副本路由、备副本路由、租户机器路由、缓存信息、路由策略、事务路由和常见问题这七部分进行介绍。
主副本路由
在分布式系统中,为了容灾高可用,会采用多副本机制。副本之间需要保证数据一致性,往往采用 Paxos 或者 Raft 算法,在工程实践中,有一个特殊副本,该副本数据最新,并控制数据在副本间的同步,这个副本叫做主副本,其它副本统称为备副本。
由于 OceanBase 数据库只有一个主副本,因此,主副本路由策略就是发往该副本。此处以 select c1 from t1 语句为例,介绍主副本路由需要满足的两个条件:
SQL 语句操作(查询、插入、更新和删除等)实体表,如上例中的 t1 表。
请求必须读到最新数据,即强读。
在 ODP 日志中,主副本路由的关键字是 ROUTE_TYPE_LEADER。要实现主副本路由,就需要知道访问的分区标识和分区所在的位置。在 ODP 的实现中,分为两种情况。
单分区表
表只有一个分区,根据表名就可以获得副本位置信息;
多分区表
表有多个分区,ODP 需要根据 SQL 中的表名和分区键计算出分区标识,之后再获取副本位置信息。副本信息包含主副本信息和备副本信息,主副本路由只需要使用主副本信息。
对于多分区路由,涉及分区方式(如 hash、range 和 list)、分区键类型(number、varchar 等)、分区算法(如 hash 算法)、类型转换(如 SQL 中的值类型和分区键类型不同)等知识点,实现比较复杂。以二级分区为例,路由分为如下 10 个步骤:
解析 SQL 获取表名
根据表名访问 OceanBase 内部表,确认是分区表
解析 SQL 中的列表达式(如 c1=1)
访问 OceanBase 内部表获取分区表信息
访问 OceanBase 内部表获取一级分区信息
根据列表达式计算一级分区的 partition_id
访问 OceanBase 内部表获取二级分区信息
根据列表达式计算二级分区的 partition_id
计算最终的 partition_id
访问 OceanBase 内部表获取对应 partition_id 的位置信息
备副本路由
和主副本路由相似,备副本路由也需要满足两个条件:
SQL 语句查询实体表,如
select c1 from t1中的 t1 表。请求要求弱读即可,即不要求读到最新数据。
这两个条件和主副本路由需要满足的条件是有区别的:
对于条件 1,备副本路由只支持查询语句,不支持其他语句,这也是 Paxos 算法的实现要求。
对于条件 2,需要主动设置弱读标记
ob_read_consistency=weak,可以通过 hint、session 等设置。
对于备副本路由,SQL 发往主副本和备副本都可以正常工作,因此备副本路由的选择变多了(多个副本选择的问题请参考 路由策略)。
和备副本相关的一个重要话题就是读写分离,请求进行读写分离后,可以降低主副本压力。ODP 也实现了读写分离功能,并在不断打磨细节,在 OceanBase 公有云等场景帮助客户解决了性能问题。读写分离详细信息请参考 读写分离。
租户机器路由
有时 ODP 无法获取主副本或备副本,此时可以从租户机器中选取一台,这就是租户机器路由。常见的租户机器路由场景如下:
SQL 本身不包含表名,如
select 1语句。主副本或者备副本所在的机器有故障。
ODP 本身功能限制,如复杂 SQL 无法获得表名,无法走副本路由。
通过租户机器路由,ODP 将 SQL 发往租户所在的机器,因此可以保障功能正常。租户机器路由和备副本路由一样,有多个副本可以选择,也存在着路由策略的问题。
缓存信息
主副本路由、备副本路由和租户机器路由都需要通过 sys 租户查询副本路由信息,为了提升性能和降低对 sys 租户的压力,ODP 对路由信息做了缓存。
对于缓存信息,最重要的是时效性。sys 租户的缓存信息可以通过定期访问内部表创建和刷新,但是副本路由的缓存信息却不可以采用此策略,主要问题是拉取副本路由信息的 SQL 太多,会对 sys 租户造成很大的压力。
SQL 数量 = 副本的个数 * ODP 数量。
OceanBase 数据库可以支持十万、百万分区,分区数极大。因此,缓存时效性对 ODP 是一大难题,使用了过期的缓存信息就会出现常说的“路由不准”的问题。那么,怎么保证缓存的时效性呢?
我们先看一下缓存信息在 ODP 中的内容。使用 root@proxysys 账号登录 ODP,通过 show proxyroute 命令可以查看表的缓存信息,如下:
MySQL [(none)]> show proxyroute like 'ob1.hudson tt1 test sbtest1'\G
*************************** 1. row ***************************
cluster_name: ob1.hudson
tenant_name: tt1
database_name: test
table_name: sbtest1
state: AVAIL
partition_num: 1
replica_num: 3
table_id: 1101710651081698
cluster_version: 2
schema_version: 1649196335597728
from_rslist: N
create_time: 2022-04-07 12:41:16
last_valid_time: 2022-04-07 12:41:16
last_access_time: 2022-04-07 12:41:16
last_update_time: 1970-01-01 08:00:00
expire_time: 2022-04-12 12:48:42
relative_expire_time: 2022-04-07 12:40:41
server addr: server[0]=xx.xx.xx.xx:xx,leader,FULL; server[1]=xx.xx.xx.xx:xx,follower,FULL; server[2]=xx.xx.xx.xx:xx,follower,FULL;
这个例子展示了缓存包含的重要信息:集群名、租户名、库名、表名、分区数、副本数、时间、地址信息和缓存状态等。其中,缓存状态是需要重点关注的对象,缓存策略都是通过修改状态信息实现的,这些状态影响缓存的刷新机制。缓存信息分为如下 5 个状态。
BUILDING状态:缓存正在创建,需等待创建完成然后使用。AVAIL状态:缓存正常,直接使用即可。DIRTY状态:缓存失效,信息不准确。UPDATING状态:失效的缓存正在更新过程中。DELETED状态:缓存已经被删除,不可以使用,后续会被清理掉。
通过修改缓存状态可以刷新缓存状态,从而保证时效性,下面从创建、淘汰和刷新这三方面介绍缓存刷新机制。
缓存创建:首次访问分区时,ODP 通过查询 sys 租户的
__all_virtual_proxy_schema获得,指定表名为真实表名,注意和租户路由信息部分区分,创建好后缓存状态为AVAIL。缓存淘汰:当 OBServer 节点返回路由不准时(OBServer 节点会通过 OK 报文中携带的
is_partition_hit字段反馈),ODP 修改缓存状态为DIRTY。缓存刷新:当缓存信息变为
DIRTY状态后,淘汰过期缓存,并重新创建或者更新缓存信息。
说明
目前缓存淘汰主要通过 OBServer 节点的报文反馈实现,这样就无法实时感知,只有出现一次“错误”路由后,才能刷新,这也是容易出问题一个地方。
事务路由
上文介绍了单个 SQL 的路由策略,有些功能(如事务功能)包含一条或多条 SQL,此时就需要使用到事务路由。
ODP 针对事务有两种路由方式,一种是将同一个事务的语句统一路由到一个数据节点上执行;另一种是将事务中的语句拆分,路由到不同的数据节点执行,通过 ODP 同步不同数据节点事务的状态,完成事务的拆分执行。
说明
事务路由依赖 OceanBase 2.0 协议进行事务状态的同步。
事务路由节点选择
事务路由的执行节点分为协调者和参与者两种节点。协调者节点即事务的开启节点,负责执行会影响事务状态的非 DML 语句,比如 BEGIN、START TRANSACTION、COMMIT、ROLLBACK 等;参与者节点负责执行事务中不会影响事务状态的 DML 语句。
ODP 包含简单的 SQL Parser,能够解析出 SQL 语句是否为 DML 语句,ODP 解析出一条语句属于 DML 语句之后,会通过表路由或者 LDC 路由将语句路由到合适的节点,被路由到的节点即成为事务的参与者节点。而事务的协调者节点即事务中第一条语句的执行节点。
说明
LDC 路由信息请参考 路由策略。
事务路由配置
默认情况下事务路由的功能是开启的,可以使用以下语句查询、运维事务路由的功能。
在客户端中使用 root 用户登录集群的 sys 租户。
检查修改 OceanBase 2.0 协议的配置,确保 OceanBase 2.0 协议启用。
obclient> SHOW PROXYCONFIG LIKE enable_ob_protocol_v2; obclient> ALTER PROXYCONFIG SET enable_ob_protocol_v2=True;执行以下语句对 ODP 的事务路由进行配置。
obclient> SHOW PROXYCONFIG LIKE enable_transaction_internal_routing; obclient> ALTER PROXYCONFIG SET enable_transaction_internal_routing=True;
常见问题
在进行租户内路由时,有以下几项常见问题:
无法获取表名(主副本路由)
SQL 太复杂,目前 ODP 无法识别所有的 SQL 语句。
SQL 太长,ODP 存储 SQL 的 buffer 只有 4k,SQL 太长不会全解析。
分区计算失败(主副本路由)
ODP 无法支持多分区键的计算,如 range(c1,c2)。
SQL 语句中没有分区键的表达式或 ODP 未提取出来。
分区键表达式 ODP 无法处理,如 c1=now(),ODP 还未支持 now 函数。
使用过期缓存(缓存信息)
ODP 无主动刷新机制。
OBServer 节点未进行路由反馈,如分布式计划 OBServer 节点不反馈。
配置错误:
未设置路由策略为
FOLLOWER_FIRST,弱读发往了主副本(备副本路由)。未设置 LDC 路由信息或者信息设置错误导致跨机房或者跨城(路由策略)。