---
title: "强读路由 - 数据库代理  V4.3.6 | OceanBase 文档中心"
description: 强读路由 本文介绍什么是强读路由，并结合示例介绍 ODP 强读路由选取的过程。 背景介绍 在 OceanBase 数据库中，分区（Partition）是数据存储的基本单元。当您创建表时，就会存在表和分区 (Partition) 的映射。如果创建的是非分区表，一张表仅对应一个分区 (Partition) ，如果创建的是…
---
切换语言

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

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

# 强读路由

更新时间：2026-04-10 16:11:03

[编辑](https://github.com/oceanbase/oceanbase-proxy-doc/edit/V4.3.6/zh-CN/600.data-routing/400.intra-tenant-routing/300.strong-read-partition-table-routing.md)  

本文介绍什么是强读路由，并结合示例介绍 ODP 强读路由选取的过程。

## 背景介绍

在 OceanBase 数据库中，分区（Partition）是数据存储的基本单元。当您创建表时，就会存在表和分区 (Partition) 的映射。如果创建的是非分区表，一张表仅对应一个分区 (Partition) ，如果创建的是分区表，一张表可能会对应多个分区 (Partition) 。

目前 OceanBase 数据库未实现分区 (Partition) 的合并和分裂。假设客户端请求某表的 P0 分区，P0 分区在 OBServer0 节点和 OBServer2 节点上，ODP 却将请求路由到 OBServer1 节点，因为 OBServer1 节点不存在该分区数据，所以 ODP 会将请求再次路由到 OBServer0 节点或 OBServer2 节点，从而产生远程计划（Remote Plan）。而 ODP 若保存着自 P0 分区到 OBServer0 节点和自 P0 分区到 OBServer2 节点的映射，将会保证将请求路由到存在该分区数据的 OBServer 节点，避免远程计划。

路由时仅路由到分区所在的 OBServer 节点还不够，还需要知道分区的主备信息，每个分区在 OceanBase 数据库中存在主副本（Leader）和备副本（Follower），他们分布在不同的 OBServer 节点上面，假设 OBServer0 节点为主副本、OBServer2 节点为备副本，此时一个强读请求被路由到了备副本，备副本会将请求路由至主副本，产生远程计划。ODP 可以区分强读/弱读请求，将强读请求发往主副本，避免远程计划。

#### 说明

在 OceanBase 数据库中，有 Local 计划、Remote 计划和 Distributed 计划三种表路由。Local 计划、Remote 计划均为单分区的路由。ODP 的作用就是尽量消除 Remote 计划，将路由尽可能地变为 Local 计划。如果表路由类型为 Remote 计划的 SQL 过多，说明该 ODP 的路由可能存在问题（可通过查看 `oceanbase.GV$OB_SQL_AUDIT` 视图中 `plan_type` 字段来确认表路由类型）。表路由的详细介绍可参见 OceanBase 数据库文档 [ODP 表路由](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000218515) 一文。

## 路由选取

客户端发送分区表强读请求 SQL，SQL 语句提供分区键值、表达式或分区名，ODP 解析出分区 ID，通过分区 ID 查找并将请求路由到对应 Leader 副本位置。本节结合几个示例介绍 ODP 对强读请求的路由过程。

- SQL 语句中提供分区键列值

  例如 `T0` 表分区键为 `C1`，查询语句可以为 `SELECT * FROM T0 WHERE C1 = xxxx;`。具体路由选取过程可参见 [示例一：SQL 语句中提供分区键值](#示例一：SQL%20语句中提供分区键值)。
 - SQL 语句中提供分区键列表达式

  例如 `T0` 表分区键为 `C1`，查询语句可以为 `SELECT * FROM T0 WHERE C1 = ABS(xxxx);`。具体路由选取过程可参见 [示例二：SQL 语句中提供分区键表达式](#示例二：SQL%20语句中提供分区键表达式)。
 - SQL 语句中提供分区名称

  例如 `T0` 表一级分区名称有 `P0`、`P1`、`P2`，二级分区名称有 `SP0`、`SP1`、`SP2`，查询语句可以为 `INSERT INTO T0 PARTITION(P0SSP2) VALUES(xxxx);`。具体路由选取过程可参见 [示例三：SQL 语句中提供分区名称](#示例三：SQL%20语句中提供分区名称)

  #### 注意

     - 指定分区名称的语法为 `SELECT/UPDATE/INSERT ... table_name PARTITION(partition_name[Ssubpartition_name])`，分区表的一级分区默认名称为 P0、P1、P2 等，二级分区默认名称为 SP0、SP1、SP2 等。
     - 使用指定分区名称路由时如果不指定二级分区名称，则删去语句中标识指定二级分区名的前缀字符 `S`。
 - Multi-Stmt 语句

  ODP 自 V4.3.1.5（V4.3.1 系列）和 V4.3.5（V4.3.5 系列）起，对 Multi-Stmt 的路由计算进行优化：当第一条语句为事务开启时（`begin`/`start transaction`），使用第二条语句作为路由计算依据；当第一条语句为非事务开启时，使用 Multi-Stmt 的第一条语句作为路由计算的依据。具体路由选取过程可参见 [示例四：Multi-Stmt 语句](#示例四：Multi-Stmt%20语句)。

### 示例一：SQL 语句中提供分区键值

1. 登录 OceanBase 数据库，执行如下命令创建分区表。

   ```sql
   obclient [test]> CREATE TABLE T0(C1 INT) PARTITION BY HASH(C1) PARTITIONS 8;

   ```
 2. 为将要执行的 SQL 语句提供分区键值。

   ```sql
   obclient [test]> SELECT /* +READ_CONSISTENCY(STRONG) */ * FROM T0 WHERE C1=123;

   ```
 3. 使用 EXPLAIN ROUTE 命令查看 ODP 路由选取的过程。

   ```sql
   obclient [test]> EXPLAIN ROUTE SELECT /* +READ_CONSISTENCY(STRONG) */ * FROM T0 WHERE C1=123\G

   ```

   输出如下，ODP 将 SQL 语句路由至分区主副本。

   ```sql
   *************************** 1. row ***************************
   ...
   Route Plan
   -----------------
   > SQL_PARSE:{cmd:"COM_QUERY", table:"T0"}
   > ROUTE_INFO:{route_info_type:"USE_PARTITION_LOCATION_LOOKUP"}
   > LOCATION_CACHE_LOOKUP:{mode:"oceanbase"}
     > TABLE_ENTRY_LOOKUP_DONE:{table:"T0", table_id:500084, partition_num:8, table_type:"USER TABLE", entry_from_remote:false}
     > PARTITION_ID_CALC_START:{}
       > EXPR_PARSE:{col_val:"C1=123"}
       > RESOLVE_EXPR:{part_range:"[123 ; 123]"}
         > RESOLVE_TOKEN:{token_type:"TOKEN_INT_VAL", resolve:{"BIGINT":123}, token:"123"}
       > CALC_PARTITION_ID:{part_description:"partition by hash(INT(binary)) partitions 8"}
     > PARTITION_ID_CALC_DONE:{partition_id:200065, level:1, partitions:"(p3)"}
     > PARTITION_ENTRY_LOOKUP_DONE:{leader:"10.10.10.1:50109", entry_from_remote:false}
   > ROUTE_POLICY:{chosen_route_type:"ROUTE_TYPE_LEADER"}
   > CONGESTION_CONTROL:{svr_addr:"10.10.10.1:50109"}

   ```

### 示例二：SQL 语句中提供分区键表达式

1. 登录 OceanBase 数据库，执行如下命令创建分区表。

   ```
 2. 为将要执行的 SQL 语句提供分区键表达式。

   ```sql
   obclient [test]> SELECT /* +READ_CONSISTENCY(STRONG) */ * FROM T0 WHERE C1=ABS(123);  

   ```

   输出如下，ODP 完成 ABS() 函数计算后，路由至分区主副本。

   ```

### 示例三：SQL 语句中提供分区名称

1. 登录 OceanBase 数据库，执行如下命令创建分区表。

   ```

   创建分区表时未指定分区名称，将会使用默认分区名称。
 2. 为将要执行的 SQL 语句提供分区名称。

   ```sql
   obclient [test]> SELECT /* +READ_CONSISTENCY(STRONG) */  * FROM T0 PARTITION(P1) WHERE C1=123;

   ```sql
   obclient [test]> EXPLAIN ROUTE SELECT * FROM T0 PARTITION(p1) WHERE C1=123\G

   ```

   输出如下，ODP 将 SQL 语句路由至 P0 分区。

   ```sql
   Trans Current Query:"EXPLAIN ROUTE SELECT * FROM T0 PARTITION(p1) WHERE C1=123"

   Route Prompts
   -----------------
   > ROUTE_INFO
     [INFO] Will route to partition server or routed by route policy
   > PARTITION_ID_CALC_DONE
     [INFO] Will route to specified partition name(p1)

   Route Plan
   -----------------
   > SQL_PARSE:{cmd:"COM_QUERY", table:"T0"}
   > ROUTE_INFO:{route_info_type:"USE_PARTITION_LOCATION_LOOKUP"}
   > LOCATION_CACHE_LOOKUP:{mode:"oceanbase"}
     > TABLE_ENTRY_LOOKUP_DONE:{table:"T0", table_id:500084, partition_num:8, table_type:"USER TABLE", entry_from_remote:false}
     > PARTITION_ID_CALC_DONE:{partition_id:200063, level:1, part_name:"p1"}
     > PARTITION_ENTRY_LOOKUP_DONE:{leader:"10.10.10.3:50111"}
   > ROUTE_POLICY:{chosen_route_type:"ROUTE_TYPE_LEADER"}
   > CONGESTION_CONTROL:{svr_addr:"10.10.10.3:50111"}

   ```

   `Will route to specified partition name(p1)` 说明 ODP 将请求路由至语句中指定的分区名的分区。

### 示例四：Multi-Stmt 语句

    事务开启   非事务开启

1. 登录 OceanBase 数据库，执行如下命令创建分区表

   ```sql
   obclient [test]> CREATE TABLE T0(C1 INT, C2 VARCHAR(10)) PARTITION BY HASH(C1) PARTITIONS 8;
   obclient [test]> CREATE TABLE T1(C1 INT, C2 VARCHAR(10)) PARTITION BY HASH(C1) PARTITIONS 8;

   ```
 2. 执行 Multi-Stmt

   ```sql
   BEGIN;
   UPDATE T0 SET C2 = 'obproxy' WHERE C1=123;
   INSERT INTO T1 (C1, C2) VALUES (12, 'odp');
   COMMIT;

   ```
 3. 查看诊断日志

   #### 说明

   需将 [route_diagnosis_level](https://www.oceanbase.com/docs/common-odp-doc-cn-1000000005053963) 配置项修改为 `4` 才可在 `obproxy_diagnosis.log` 日志中查看到对应路由信息，具体介绍可参见 [获取诊断信息](https://www.oceanbase.com/docs/common-odp-doc-cn-1000000005054016) 一文。

   从诊断日志（`obproxy_diagnosis.log`）中获取执行对应日志的时间戳（此处示例为 `2025-06-23 17:57:11.647321`），执行如下命令将对应日志转化为树状诊断过程，方便查看。

   ```shell
   grep "2025-06-23 17:57:11.647321" obproxy_diagnosis.log | sed "s/\/n/\n/g"

   ```

   输出如下，可以看到事务开启的情况下，ODP 使用 Multi-Stmt 的第二条语句作为路由计算依据，将语句路由到了 `t0` 表。

   ```shell
   [2025-07-21 14:35:55.606826] [7477][Y0-00007F27DFF20760] [ROUTE]([ROUTE](*route_diagnosis=
   Trans Current Query:"BEGIN;UPDATE T0 SET C2 = 'obproxy' WHERE C1=123;INSERT INTO T1 (C1, C2) VALUES (12, 'odp');COMMIT;"

   Route Prompts
   > ROUTE_INFO
     [INFO] Will do table partition location lookup to decide which OBServer to route to
   > ROUTE_POLICY
     [INFO] Will route to table's partition leader replica(10.10.10.1:2881) using route policy PRIMARY_ZONE_FIRST because query for STRONG read
   > CONGESTION_CONTROL
     [INFO] This replica(10.10.10.1:2881) is no need to pass congestion control

   Route Plan
   > SQL_PARSE:{cmd:"OB_MYSQL_COM_QUERY", table:"t0"}
   > 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, first part_expr:"c1"}
     > TABLE_ENTRY_LOOKUP_DONE:{table:"t0", table_id:"500038", table_type:"USER TABLE", partition_num:8}
     > PARTITION_ID_CALC_START:{}
       > EXPR_PARSE:{col_val:"C2=obproxy,C1=123"}
         > RESOLVE_TOKEN:{token_type:"TOKEN_STR_VAL", resolve:"VARCHAR:obproxy<utf8mb4_general_ci>", token:"obproxy"}
         > RESOLVE_TOKEN:{token_type:"TOKEN_INT_VAL", resolve:"BIGINT:123", token:"123"}
       > CALC_PARTITION_ID:{part_description:"partition by hash(INT<binary>) partitions 8"}
     > PARTITION_ID_CALC_DONE:{partition_id:200016, level:1, partitions:"(p3)", parse_sql:"UPDATE T0 SET C2 = 'obproxy' WHERE C1=123;"}
     > PARTITION_ENTRY_LOOKUP_DONE:{leader:"10.10.10.1:2881"}
   > ROUTE_POLICY:{route_policy:"", chosen_route_type:"ROUTE_TYPE_LEADER", type:"FULL"}
   > CONGESTION_CONTROL:{svr_addr:"10.10.10.1:2881", need_congestion_lookup:false}
   > HANDLE_RESPONSE:{is_parititon_hit:"true", send_action:"SERVER_SEND_REQUEST", state:"TRANSACTION_COMPLETE"}
   )

   ```

1. 登录 OceanBase 数据库，执行如下命令创建两个分区表 `t0`、`t1`

   ```shell
   obclient [test]> CREATE TABLE t0(C1 INT, C2 VARCHAR(10)) PARTITION BY HASH(C1) PARTITIONS 8;
   obclient [test]> CREATE TABLE t1(C1 INT, C2 VARCHAR(10)) PARTITION BY HASH(C1) PARTITIONS 8;

   ```shell
   INSERT INTO t1 (C1, C2) VALUES (1, 'ob'), (2, 'odp');
   UPDATE t0 SET C2 = 'odp' WHERE C1=123;

   从诊断日志（`obproxy_diagnosis.log`）中获取执行对应日志的时间戳（此处示例为 `2025-06-24 14:07:02.793704`），执行如下命令将对应日志转化为树状诊断过程，方便查看。

   ```shell
   grep "2025-06-24 14:07:02.793704" obproxy_diagnosis.log | sed "s/\/n/\n/g"

   ```

   输出如下，可以看到非事务开启的情况下，ODP 使用 Multi-Stmt 的第一条语句作为路由计算依据，将语句路由到了 `t1` 表。

   ```shell
   [2025-06-24 14:07:02.793704] [10559][Y0-00007FDDF9C95760] [ROUTE]([ROUTE](*route_diagnosis=
   Trans Current Query:"INSERT INTO t1 (C1, C2) VALUES (1, 'ob'), (2, 'odp');UPDATE t0 SET C2 = 'odp' WHERE C1=123;"

   Route Prompts
   > ROUTE_INFO
     [INFO] Will do table partition location lookup to decide which OBServer to route to
   > ROUTE_POLICY
     [INFO] Will route to table's partition leader replica(10.10.10.1:4881) using route policy PRIMARY_ZONE_FIRST because query for    STRONG read

   Route Plan
   > SQL_PARSE:{cmd:"OB_MYSQL_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, first part_expr:"c1"}
     > TABLE_ENTRY_LOOKUP_DONE:{table:"t1", table_id:"500011", table_type:"USER TABLE", partition_num:8}
     > PARTITION_ID_CALC_START:{}
       > EXPR_PARSE:{col_val:"C1=1,C2=ob"}
         > RESOLVE_TOKEN:{token_type:"TOKEN_INT_VAL", resolve:"BIGINT:1", token:"1"}
         > RESOLVE_TOKEN:{token_type:"TOKEN_STR_VAL", resolve:"VARCHAR:ob<utf8mb4_general_ci>", token:"ob"}
       > CALC_PARTITION_ID:{part_description:"partition by hash(INT<binary>) partitions 8"}
     > PARTITION_ID_CALC_DONE:{partition_id:200010, level:1, partitions:"(p1)"}
     > PARTITION_ENTRY_LOOKUP_DONE:{leader:"10.10.10.1:4881"}
   > ROUTE_POLICY:{route_policy:"", chosen_route_type:"ROUTE_TYPE_LEADER", type:"FULL"}
   > CONGESTION_CONTROL:{svr_addr:"10.10.10.1:4881"}
   > HANDLE_RESPONSE:{is_parititon_hit:"true", send_action:"SERVER_SEND_REQUEST", state:"TRANSACTION_COMPLETE"}
   )

   ```

    上一篇 下一篇 ![有帮助](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) 咨询热线
