---
title: "租户内路由 | OceanBase 文档中心"
description: 租户内路由 租户内路由是指在获取租户的机器列表后，选择合适的节点执行 SQL。 对于租户内路由，ODP 可以像普通代理（如 HAProxy）一样，一个客户端连接对应一个服务端连接，服务端机器从租户机器列表选取，这样功能就会简单很多，但无法满足性能和高可用要求，可见路由影响了连接管理。 租户内路由是路由功能最复杂的部分…
---
切换语言

- 中文站 - 简体中文
- International - English
- 日本站 - 日本語

文档反馈![](https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*nSAhQbrBXKwAAAAAAAAAAAAADiGDAQ/original) 数据库代理 V 4.0.0 企业版

# 租户内路由

更新时间：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 个步骤：

1. 解析 SQL 获取表名
 2. 根据表名访问 OceanBase 内部表，确认是分区表
 3. 解析 SQL 中的列表达式（如 c1=1）
 4. 访问 OceanBase 内部表获取分区表信息
 5. 访问 OceanBase 内部表获取一级分区信息
 6. 根据列表达式计算一级分区的 partition_id
 7. 访问 OceanBase 内部表获取二级分区信息
 8. 根据列表达式计算二级分区的 partition_id
 9. 计算最终的 partition_id
 10. 访问 OceanBase 内部表获取对应 partition_id 的位置信息

## 备副本路由

和主副本路由相似，备副本路由也需要满足两个条件：

1. SQL 语句查询实体表，如 `select c1 from t1` 中的 t1 表。
 2. 请求要求弱读即可，即不要求读到最新数据。

这两个条件和主副本路由需要满足的条件是有区别的：

- 对于条件 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` 命令可以查看表的缓存信息，如下：

```sql
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 中）所在的机器。出现这种路由策略主要有以下两个原因：

1. OceanBase 中常用的高性能部署架构是租户的 Primary Zone 在一台机器，这样可以避免分布式系统的很多网络开销；
 2. 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 如下：

```sql
alter system modify zone "z1" set region = "SHANGHAI";
alter system modify zone "z1" set idc = "zue";

```

结果如下：

```sql
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 集群信息失败](https://www.oceanbase.com/docs/enterprise-oceanbase-ocp-cn-10000000002099251)。

##### ODP 设置 LDC

ODP 通过配置项或者启动参数设置 LDC 信息，以 zue 为示例机房介绍这两种操作方法。

- 方法一：ODP 进程启动时通过 `-o` 参数指定设置 LDC 信息

  ```shell
  cd /opt/taobao/install/obproxy
  ./bin/obproxy -o proxy_idc_name=zue

  ```
 - 方法二：通过执行 SQL 语句设置 LDC 信息

  ```sql
  mysql> alter proxyconfig set proxy_idc_name='zue';

  ```

通过 ODP 执行内部命令 `show proxyinfo idc;` 可以检查 ODP 内部识别的 LDC 部署情况。

```sql
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 不再进行路由，直接发往第一条语句发往的节点。

至于为什么事务路由只能发往第一条语句发往节点，您可参考 [数据路由影响因素](https://www.oceanbase.com/docs/enterprise-odp-enterprise-cn-10000000000982781) 中的 **功能因素**。目前 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 路由信息或者信息设置错误导致跨机房或者跨城（路由策略）。

 上一篇 下一篇 ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
