---
title: 如何判断 Batch DML 是否使用了 ArrayBinding 协议及 Batch 成组执行优化-OceanBase数据库使用指南
description: 了解OceanBase数据库在实际应用中关于如何判断 Batch DML 是否使用了 ArrayBinding 协议及 Batch 成组执行优化相关的常见问题和使用技巧，帮助您快速解决如何判断 Batch DML 是否使用了 ArrayBinding 协议及 Batch 成组执行优化的难题。
---
切换语言

- 简体中文
- English

划线反馈

# 如何判断 Batch DML 是否使用了 ArrayBinding 协议及 Batch 成组执行优化

更新时间：2026-05-28 02:06

适用版本： V4.0.x、V4.1.x、V4.2.x、V4.3.x、V4.4.x 内容类型：TechNote  

为了减少客户端和数据库之间的 RPC 交互和执行上下文切换的开销，OceanBase 提供了 `Batch DML` 执行功能来优化开销。OceanBase 数据库 Oracle 模式下，支持了 `ArrayBinding` 协议，通过给一条语句绑定一组 Array 参数来达到批量执行的目的；OceanBase 数据库 MySQL 模式下，支持了 `Multi Queries` 协议，通过将参数改写成以`;`分割的多条语句，以一个 `Multi Queries` 协议包的形式发送给 OBServer，并依次执行 `Multi Qeries` 协议包中的语句，并返回对应的结果。 本文主要介绍在实际业务中如何判断 `Batch DML` 是否使用了 `ArrayBinding` 协议及 Batch 成组执行优化，进而评估 DML 的性能是否符合预期。

## 详细说明

### 使用 ArrayBinding 的先决条件

**按照当前设计及 `ArrayBinding` 实现上的限制，使用 `ArrayBinding` 协议及 Batch 成组执行优化的先决条件有以下几个：**

- OceanBase 数据库 Oracle 模式租户。
 - 租户级参数 `ob_enable_batched_multi_statement` 需要打开（如果没有开启该配置项的话 OBServer 内核不会做 Batch 成组执行优化）。
 - Plan Cache 需要打开，不能禁用。
 - OB-JDBC URL 中需要添加 `useArrayBinding=true`。
 - 业务代码中连接成功后（在执行 `Batch DML` 之前）执行过 `connection.setAutoCommit(false)`。
 - 业务代码中使用了 `addBatch()` 和 `executeBatch()` 来尝试批量执行。
 - `ArrayBinding` 还需要开启 PS 二合一协议。

**备注：OB-JDBC 连接串设置，满足以下两个任意条件都会自动开启 PS 二合一协议：**

- `useServerPrepStmts=true&usePieceData=true`。
 - `useOraclePrepareExecute=true`（仅限 Oracle 租户）。、

### 如何判断 Batch DML 是否成功使用了 ArrayBinding 协议及 Batch 成组执行优化

#### 方法一：检查对应的 SQL Audit 记录中的 `query_sql` 和 `affected_rows` 字段

**测试 Batch Insert 是否走了 ArrayBinding 协议，测试代码如下：**

```shell
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.text.SimpleDateFormat;

public class TestOBOracleBatch {
    public static void main(String[] args) {
        try {
            String driver = "com.oceanbase.jdbc.Driver";
            String url = "jdbc:oceanbase://xx.xxx.80.111:2881/ALVIN?useArrayBinding=true&useOraclePrepareExecute=true";
            String user = "ALVIN@oracle";
            String password = "xxx";
            Class.forName(driver);
            Connection connection = DriverManager.getConnection(url, user, password);
            connection.setAutoCommit(false);
            /*
CREATE TABLE emp (
    empno   NUMBER(4) CONSTRAINT PK_EMP PRIMARY KEY,
    ename   VARCHAR2(10),
    job     VARCHAR2(9),
    mgr     NUMBER(4),
    hiredate DATE,
    sal     NUMBER(7,2),
    comm    NUMBER(7,2),
    deptno  NUMBER(2)
);

create index emp_ename_idx on emp(ename);
*/
            PreparedStatement pstmt = connection.prepareStatement("INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");

            // 批量设置参数
            pstmt.setInt(1, 7369);
            pstmt.setString(2, "SMITH");
            pstmt.setString(3, "CLERK");
            pstmt.setInt(4, 7902);
            pstmt.setDate(5, java.sql.Date.valueOf("1980-12-17"));
            pstmt.setDouble(6, 800.00);
            pstmt.setDouble(7, 0.00);
            pstmt.setInt(8, 20);
            pstmt.addBatch();

            pstmt.setInt(1, 7499);
            pstmt.setString(2, "ALLEN");
            pstmt.setString(3, "SALESMAN");
            pstmt.setInt(4, 7698);
            pstmt.setDate(5, java.sql.Date.valueOf("1981-02-20"));
            pstmt.setDouble(6, 1600.00);
            pstmt.setDouble(7, 300.00);
            pstmt.setInt(8, 30);
            pstmt.addBatch();

            pstmt.setInt(1, 7521);
            pstmt.setString(2, "WARD");
            pstmt.setString(3, "SALESMAN");
            pstmt.setInt(4, 7698);
            pstmt.setDate(5, java.sql.Date.valueOf("1981-02-22"));
            pstmt.setDouble(6, 1250.00);
            pstmt.setDouble(7, 500.00);
            pstmt.setInt(8, 30);
            pstmt.addBatch();

            // 执行批量操作
            pstmt.executeBatch();
            System.out.println("Succ");

            connection.commit();
            pstmt.close();
            connection.close();
        }
        catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

```

**编译执行如下：**

```shell
[admin@observer1 /home/admin]$ javac -cp ./oceanbase-client-2.4.14.1.jar TestOBOracleBatchInsert.java

[admin@observer1 /home/admin]$ java -cp ./oceanbase-client-2.4.14.1.jar:./ TestOBOracleBatchInsert
Batch insert succeeded !

```

**检查对应的 SQL Audit 执行记录：**

```shell
MySQL [oceanbase]> select usec_to_time(request_time),plan_id,is_hit_plan,trace_id,query_sql,params_value,affected_rows,is_batched_multi_stmt,PS_CLIENT_STMT_ID,ps_inner_stmt_id from gv$ob_sql_audit where tenant_id=1004 and db_name='alvin' order by request_time\G
...
*************************** 8. row ***************************
usec_to_time(request_time): 2025-07-23 15:39:49.147114
                   plan_id: 0
               is_hit_plan: 0
                  trace_id: YB420BA6506F-000639F24CDECF80-0-0
                 query_sql: set autocommit = 0
              params_value:
             affected_rows: 0
     is_batched_multi_stmt: 0
         PS_CLIENT_STMT_ID: -1
          ps_inner_stmt_id: -1
*************************** 9. row ***************************
usec_to_time(request_time): 2025-07-23 15:39:49.180474
                   plan_id: 4021
               is_hit_plan: 0
                  trace_id: YB420BA6506F-000639F24CDECF81-0-0
                 query_sql: INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
              params_value: NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
             affected_rows: 3
     is_batched_multi_stmt: 1
         PS_CLIENT_STMT_ID: 1
          ps_inner_stmt_id: -1
*************************** 10. row ***************************
usec_to_time(request_time): 2025-07-23 15:39:49.207106
                   plan_id: 0
               is_hit_plan: 0
                  trace_id: YB420BA6506F-000639F24CDECF82-0-0
                 query_sql: COMMIT
              params_value:
             affected_rows: 0
     is_batched_multi_stmt: 0
         PS_CLIENT_STMT_ID: -1
          ps_inner_stmt_id: -1
10 rows in set (0.06 sec)

```

可以发现：这边 `query_sql: INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES (?, ?, ?, ?, ?, ?, ?, ?)` 只包含了一对`()`，即一条记录，但是 `affected_rows: 3` 表明实际插入了 3 条记录，说明该 SQL 走了 `ArrayBinding` 协议和 Batch 成组执行优化。

#### 方法二：配合方法一检查 SQL Audit 和 Plan Cache 中的 `is_batched_multi_stmt` 字段

**配合方法一，可以进一步检查 SQL Audit 记录和 Plan Cache 中的 `is_batched_multi_stmt` 字段是否为 1，如果为 1，则表明走到了 Batch 成组执行优化：**

```shell
MySQL [oceanbase]> select * from gv$ob_plan_cache_plan_stat where plan_id=4021\G
*************************** 1. row ***************************
              TENANT_ID: 1004
                 SVR_IP: xx.xxx.80.111
               SVR_PORT: 2882
                PLAN_ID: 4021
                 SQL_ID: D0DC764705DA5AC12F3E22F7AFE730F0
                   TYPE: 1
      IS_BIND_SENSITIVE: 0
          IS_BIND_AWARE: 0
                  DB_ID: 500192
              STATEMENT: INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
              QUERY_SQL: INSERT INTO emp (empno, ename, job, mgr, hiredate, sal, comm, deptno) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
         SPECIAL_PARAMS:
            PARAM_INFOS: {1,0,0,-1,0},{1,0,0,-1,0},{1,0,0,-1,0},{1,0,0,-1,0},{1,0,0,-1,0},{1,0,0,-1,0},{1,0,0,-1,0},{1,0,0,-1,0}
               SYS_VARS: 45,45,2151677954,+08:00,2,4,1,0,0,3,1,0,1,10485760,1,0,YYYY-MM-DD HH24:MI:SS,YYYY-MM-DD HH24:MI:SS.FF9,YYYY-MM-DD HH24:MI:SS.FF TZR TZD,BINARY,BINARY,AL32UTF8,AL16UTF16,BYTE,FALSE,1,100,64,200,0,13,NULL,1,1,1,1,1,0,0,0,1000,BLOOM_FILTER,RANGE,IN,1,17180001539,17180001539,1,45,0,0,
                CONFIGS: 3,1,1,0,1,1,0,0,30,17180001540,10,0,0,0,1,
              PLAN_HASH: 3290318591324336132
        FIRST_LOAD_TIME: 2025-07-23 15:39:49.198406
         SCHEMA_VERSION: 1753256363021920
       LAST_ACTIVE_TIME: 2025-07-23 15:39:49.199940
           AVG_EXE_USEC: 19719
       SLOWEST_EXE_TIME: 2025-07-23 15:39:49.199940
       SLOWEST_EXE_USEC: 19719
             SLOW_COUNT: 0
              HIT_COUNT: 0
              PLAN_SIZE: 78064
             EXECUTIONS: 1
             DISK_READS: 0
          DIRECT_WRITES: 0
            BUFFER_GETS: 0
  APPLICATION_WAIT_TIME: 0
  CONCURRENCY_WAIT_TIME: 0
      USER_IO_WAIT_TIME: 0
         ROWS_PROCESSED: 3
           ELAPSED_TIME: 19719
               CPU_TIME: 19692
           LARGE_QUERYS: 0
   DELAYED_LARGE_QUERYS: 0
      DELAYED_PX_QUERYS: 0
        OUTLINE_VERSION: 0
             OUTLINE_ID: -1
           OUTLINE_DATA: /*+BEGIN_OUTLINE_DATA USE_DISTRIBUTED_DML(@"INS$1") OPTIMIZER_FEATURES_ENABLE('4.2.5.3') END_OUTLINE_DATA*/
           ACS_SEL_INFO:
             TABLE_SCAN: 0
              EVOLUTION: 0
         EVO_EXECUTIONS: 0
           EVO_CPU_TIME: 0
          TIMEOUT_COUNT: 0
             PS_STMT_ID: 24
                 SESSID: 0
            TEMP_TABLES:
             IS_USE_JIT: 0
            OBJECT_TYPE: SQL_PLAN
             HINTS_INFO: /*+   */
       HINTS_ALL_WORKED: 1
           PL_SCHEMA_ID: 0
  IS_BATCHED_MULTI_STMT: 1
              RULE_NAME:
            PLAN_STATUS: INACTIVE
ADAPTIVE_FEEDBACK_TIMES: NULL
    FIRST_GET_PLAN_TIME: 14732
         FIRST_EXE_USEC: 1704
1 row in set (0.20 sec)

```

**从 `gv$ob_sql_audit` 和 `gv$ob_plan_cache_plan_stat` 中，都可以看到 `IS_BATCHED_MULTI_STMT: 1`，说明这边还同时使用上了 Batch 成组执行优化**。

#### 方法三：检查 observer.log（需要将 `syslog_level` 调高到 TRACE 级别及以上）

**使用对应的 SQL 的 trace_id 去搜索对应执行节点的 observer.log：**

```shell
[admin@observer1 /home/admin/oceanbase/log]$ grep Yxxxxx-xxxxxx-0-0 observer.log.20250723153* | grep -i array | grep -i bind
observer.log.20250723153951878:[2025-07-23 15:39:49.200232] TRACE [SERVER] try_batch_multi_stmt_optimization (obmp_stmt_execute.cpp:1817) [71213][T1004_L0_G0][T1004][Yxxxxx-xxxxxx-0-0] [lt=13] after try batched multi-stmt optimization(ret=0, stmt_type_=2, use_plan_cache=true, optimization_done=true, enable_batch_opt=true, is_ab_returning=false, THIS_WORKER.need_retry()=false, arraybinding_size_=3)

```

**从 observer.log 日志中的关键字也可以看出，这边 Batch Insert 执行成功使用上了 ArrayBinding 协议和 Batch 成组执行优化：**

```shell
try_batch_multi_stmt_optimization
after try batched multi-stmt optimization
arraybinding_size_=3

```

#### 备注

目前 `gv$ob_sql_audit` 视图中的 `is_batched_multi_stmt` 字段有个已知的问题：即使 Batch DML 使用了 `ArrayBinding` 协议及 Batch 成组执行优化，这个 `is_batched_multi_stmt` 字段还是为 0，该问题修复在 OceanBase 数据库 V4.2.1 BP9（oceanbase-4.2.1.9-109000092024091919）、V4.2.5 BP4（oceanbase-4.2.5.4-104000082025052817）、V4.3.5 BP2（oceanbase-4.3.5.2-102000162025051417）版本。`gv$ob_plan_cache_plan_stat` 中的 `is_batched_multi_stmt` 则一直是准确的，可以优先参考使用后者。

## 适用版本

OceanBase 数据库 V4.x 版本。

Previous

[PDML 并行 DML 能力列表清单](https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000001940146)

Next

[UPDATE SQL 语句产生大量 RPC 请求问题分析的原因和解决方法](https://www.oceanbase.com/knowledge-base/oceanbase-database-1000000002398589) ![有帮助](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) 咨询热线
