首批通过分布式安全可靠测评,为关键业务系统打造
租户内路由
更新时间:2026-04-14 09:41:07
租户内路由是指在获取租户的机器列表后,选择合适的节点执行 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 节点的报文反馈实现,这样就无法实时感知,只有出现一次“错误”路由后,才能刷新,这也是容易出问题一个地方。
路由策略
路由策略介绍
路由策略用于从多副本中选择出一个合适的副本。这里的多副本,可能来自备副本路由时选择出的多个副本,也有可能是类似 select 1 from dual 这种语句,使用了租户的路由信息有多个副本(指租户的机器列表)。
路由策略主要有三种:
Primary Zone 路由:第一优先级
LDC 路由:第二优先级
随机路由:第三优先级
Primary Zone 路由
在多副本选择时,优先发往 Primary Zone(租户的属性,Primary Zone 指副本的 Leader 优先分布在 Primary Zone 中)所在的机器。出现这种路由策略主要有以下两个原因:
OceanBase 中常用的高性能部署架构是租户的 Primary Zone 在一台机器,这样可以避免分布式系统的很多网络开销;
ODP 在主副本路由时,存在找不出表名和计算不出分区的情况,通过 Primary Zone 路由可以尽量发往主副本。
LDC 路由
LDC 路由是基于地址位置的路由,其中有两个重要的概念:
IDC:表示逻辑机房概念
Region:表示城市的概念。
ODP 和 OceanBase 数据库都可以设置 LDC 信息。通过 LDC 信息,ODP 可以确定和 OceanBase 数据库的位置关系。当设置了 LDC 信息后,ODP 就会默认使用 LDC 路由。
OceanBase 数据库 LDC 设置
OceanBase 数据库的每个 Zone 都可以设置 Region 属性或 IDC 属性,Region 通常设置为城市名(大小写敏感),IDC 代表该 Zone 所处的机房信息,通常设置机房名(小写)。设置 SQL 如下:
alter system modify zone "z1" set region = "SHANGHAI";
alter system modify zone "z1" set idc = "zue";
结果如下:
mysql> select * from DBA_OB_ZONES;
+-------+----------------------------+----------------------------+--------+-----+----------------+-----------+
| ZONE | CREATE_TIME | MODIFY_TIME | STATUS | IDC | REGION | TYPE |
+-------+----------------------------+----------------------------+--------+-----+----------------+-----------+
| z1 | 2022-10-31 11:48:29.040552 | 2022-10-31 11:48:29.041609 | ACTIVE | zue | SHANGHAI | ReadWrite |
+-------+----------------------------+----------------------------+--------+-----+----------------+-----------+
| z2 | 2022-10-31 11:48:29.040552 | 2022-10-31 11:48:29.041609 | ACTIVE | zue | SHANGHAI | ReadWrite |
+-------+----------------------------+----------------------------+--------+-----+----------------+-----------+
| z3 | 2022-10-31 11:48:29.040552 | 2022-10-31 11:48:29.041609 | ACTIVE | ztg | HANGZHOU | ReadOnly |
+-------+----------------------------+----------------------------+--------+-----+----------------+-----------+
3 rows in set
说明
OceanBase 数据库中更新 LDC 的 Region 和 IDC 信息后,需同步更新 OCP 中的配置,否则 OCP 会产生告警,具体操作请参见官网 云平台 OCP 文档 告警参考/OB 告警/ob_cluster_sync_failed 获取 OB 集群信息失败。
ODP 设置 LDC
ODP 通过配置项或者启动参数设置 LDC 信息,以 zue 为示例机房介绍这两种操作方法。
方法一:ODP 进程启动时通过
-o参数指定设置 LDC 信息cd /opt/taobao/install/obproxy ./bin/obproxy -o proxy_idc_name=zue方法二:通过执行 SQL 语句设置 LDC 信息
mysql> alter proxyconfig set proxy_idc_name='zue';
通过 ODP 执行内部命令 show proxyinfo idc; 可以检查 ODP 内部识别的 LDC 部署情况。
mysql> show proxyinfo idc;
+-----------------+--------------+-----------------+-----------------+--------------------------------------------------------+-------------+--------------------+
| global_idc_name | cluster_name | match_type | regions_name | same_idc | same_region | other_region |
+-----------------+--------------+-----------------+-----------------+--------------------------------------------------------+-------------+--------------------+
| zue | obcluster | MATCHED_BY_NONE | [] | [[0]"z1", [1]"z1", [2]"z2", [3]"z2", [4]"z3", [5]"z3"] | [] | [] |
+-----------------+--------------+-----------------+-----------------+--------------------------------------------------------+-------------+--------------------+
| zue | obcluster | MATCHED_BY_IDC | [[0]"SHANGHAI"] | [[0]"z1", [1]"z1", [2]"z2", [3]"z2"] | [] | [[0]"z3", [1]"z3"] |
+-----------------+--------------+-----------------+-----------------+--------------------------------------------------------+-------------+--------------------+
| zue | obcluster | MATCHED_BY_IDC | [[0]"SHANGHAI"] | [[0]"z1", [1]"z1", [2]"z2", [3]"z2"] | [] | [[0]"z3", [1]"z3"] |
+-----------------+--------------+-----------------+-----------------+--------------------------------------------------------+-------------+--------------------+
3 rows in set
说明
如果只有一个机房,LDC 用处不大,因为默认同机房机器间延迟相同。如果在同机房要使用 LDC 路由,需要划分出 LDC 架构,并设置 OBServer 和 ODP 的 LDC 属性。如果有多个机房,就可以根据机房和城市设置 LDC。
某些特殊情况下,可以通过 trick 方法设置 LDC 信息影响 LDC 路由,但不太推荐这么做。
随机路由
通过优先级路由后,如果还有多个副本,进行随机路由即可。如未开启 Primary Zone 路由或者未设置 LDC 路由,就会直接使用随机路由。
路由配置和查看
数据路由比较复杂的一个原因是有不同的路由策略,ODP 默认策略是先进行主副本路由和备副本路由,没有副本则进行租户机器路由。如果只有一个副本被选中,则直接路由,否则根据策略路由。
对于 Primary Zone 路由和 LDC 路由,受到配置项控制:
enable_primary_zone:为 true 表示使用 Primary Zone 路由策略proxy_idc_name:内容非空(内容为 IDC 的名字)表示使用 LDC 路由
除了现有路由策略,若想使用其它路由策略,可以通过修改配置项 proxy_route_policy 控制实现,设置后新策略优先级最高。目前经常设置的其它路由策略有以下两种,都和弱读有关。
FOLLOWER_FIRST:优先发往备副本,如果无备副本可用则发往主副本。FOLLOWER_ONLY:只能发往备副本,如果无备副本可用则报错。
ODP 具体使用了什么路由策略,可以在 ODP 的日志中查看关键信息 route_type,如 ROUTE_TYPE_LEADER 表示进行了主副本路由。ROUTE_TYPE_NONPARTITION_UNMERGE_LOCAL 情况比较复杂,下面介绍主要关键字含义。
PARTITON:选取有副本数据的机器,不区分副本的类型。NONPARTITION:不关心表数据分布,任何租户机器都可以。FOLLOWER:发往备副本。LEADER:发往主副本。UNMERGE:发往不在合并状态的机器。MERGE:发往在合并状态的机器。LOCAL:发往同 IDC(机房)机器。REMOTE:发往同城不同 IDC(机房)机器。REGION:发往异地的机器。READONLY:发往 ReadOnly 属性 Zone 内机器。READWRITE:发往 ReadWrite 属性 Zone 内机器。DUP:复制表中,发往复制表所在的机器。
事务路由
上文介绍了单个 SQL 的路由策略,有些功能(如事务功能)包含一条或多条 SQL。对于事务路由,事务的第一条语句受到上述策略影响,后续 SQL 不再进行路由,直接发往第一条语句发往的节点。
至于为什么事务路由只能发往第一条语句发往节点,您可参考 数据路由影响因素 中的 功能因素。目前 ODP 还未实现事务状态迁移,所以有此限制。
常见问题
在进行租户内路由时,有以下几项常见问题:
无法获取表名(主副本路由)
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 路由信息或者信息设置错误导致跨机房或者跨城(路由策略)。