加一张临时表,OceanBase Oracle 写入居然快了 30 倍!

[复制链接]
发表于 2026-8-7 13:45:57 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
把数据同步到 OceanBase Oracle 模式时,常会遇到一种诡异的慢:JDBC batch 开了、并发也拉满了,监控里增量 RPS 恒久在 10 上下,单次写入耗时冲到二十多万毫秒,写入队列一路堆积。
问题通常不在配置,而在一个被忽视的细节:JDBC batch 调用成功,不等于数据库服务端真的做了聚集化执行。
CloudCanal v6.3.0.0(2026 年 7 月 30 日发布)引入 临时表合并模式(Staging Merge) 专门解决此问题。OceanBase Oracle 模式实测:执行写入耗时从 25 万 ms 压到约 8600 ms,快了约 30 倍,RPS 从约 10 提到 700+。本文剖析根因以及 Staging Merge 的实现方式。
batch 开了,为什么写入还是慢?

拖慢 OceanBase Oracle 写入的,通常是两个场景。
场景一:batch MERGE 在服务端"失效"

数据同步到 OceanBase Oracle 模式时,遇到主键冲突通常要用 MERGE INTO ... USING ... ON ... 实现"存在则更新、不存在则插入"。客户端代码看起来规规矩矩:
  1. ps.addBatch();
  2. ps.executeBatch();
复制代码
但 addBatch() / executeBatch() 只代表 JDBC 把请求批量发出去了,不代表服务端把多行改写成一条聚集 SQL。在 OceanBase Oracle 模式下实际排查,经常会看到服务端仍在执行 N 次单行 MERGE——也就是说,batch 在服务端层面"失效"了。

表现到监控上(优化前):


  • 增量 RPS 恒久在 10 上下,险些没有有效吞吐
  • 执行写入耗时峰值 25.4 万 ms
  • 写入队列等候时间超 10 亿 ns,数据基本堵在队列里
场景二:高频 INSERT/UPDATE/DELETE 把 batch 切碎

增量同步的数据按源端事务和日记变乱到达。哪怕同一张表短时间内涌入大量数据,只要变乱类型不绝切换:
  1. INSERT -> UPDATE -> DELETE -> UPDATE -> INSERT ...
复制代码
普通批量写入只能按操作类型分开执行。本可以合并的一组同表数据,被切成大量只有几行、几十行的小批次。直接结果:

  • 目标表被反复 INSERT / UPDATE / DELETE / MERGE,同一主键短时间内被多次探测和修改
  • executeBatch() 调用次数暴增,小事务刷盘与提交开销叠加
  • batch 越碎,参数绑定和网络交互的固定成本占比越高
根因:batch API 成功 ≠ 服务端聚集执行

很多人以为"调了 executeBatch() 就是批量写入"。实际上 JDBC batch 解决的是客户端到服务端的网络往返成本,它不保证服务端把多条 DML 合并成一条聚集化执行计划。
具体到 OceanBase Oracle 模式,这个问题集中表现在 MERGE 上:客户端发出去的是一条批量 MERGE,服务端却没有把它改写成聚集执行,而是拆成 N 次单行 MERGE 逐条跑,batch 的聚集化收益完全拿不到。这不是个例——OceanBase 官方知识库 也收录了这一现象:Oracle 模式下批量语句常常无法启用 BATCH 优化。结果就是:你以为发出去的是 1 条批量 MERGE,服务端其实跑了 1024 次单行 MERGE。
所以优化方向不是"再多开几个并发",而是让服务端真正聚集化执行——用一条聚集 SQL 把同一批数据一次处理掉。
解法:CloudCanal Staging Merge

Staging Merge 用一条统一路径同时解决上面两个问题:先把一批数据批量写入 staging 临时表,再通过聚集 SQL 修改目标表。
  1. 业务数据
  2.   -> JDBC batch insert staging table
  3.   -> DELETE / MERGE target using staging table
  4.   -> commit / 清理 session 数据
复制代码
关键在于换一种写入形态:不再指望服务端把批量 MERGE 改写成聚集执行,而是先把整批数据用批量 INSERT 灌进临时表(实测单次可写入上千行),再由聚集 SQL 一次性改完目标表——比如一条 MERGE INTO 目标表 USING 临时表,自己就是聚集执行,跟 batch 改写无关。原本的 1024 次单行 MERGE,就这样被压缩成 1 条聚集 MERGE。

为什么用临时表,不直接改目标表?

因为聚集 MERGE 需要 source 数据。OceanBase / Oracle 的 GLOBAL TEMPORARY TABLE(GTT)自然得当做这个 source:

  • 结构全局可见,数据 session 私有,不同连接互不影响
  • ON COMMIT DELETE ROWS 让连接 commit 后自动清算 staging 数据
开启方式与适用范围

Staging Merge 通过两个新参数开启:

  • useStagingMerge:开启临时表合并模式
  • stagingSchema:指定临时表所在的 schema,临时表会集中建在这个 schema 下便于管理
OceanBase for Oracle 目标端在全量、增量阶段均可启用该模式。
结果:RPS 从 10 提到 700

下面这组实测数据来自 OceanBase for Oracle(Oracle 租户模式)目标端。
同样的情况:阿里云 ECS 8C16G、184 列宽表、writeParallel 32、increBatchSize 1024,全程无 CPU / 内存瓶颈。
OB Oracle batch MERGE 失效场景
优化前:

开启 useStagingMerge 后:


  • 增量 RPS 从 ~10 提升到 479(均值)~ 718(峰值)
  • 执行写入耗时从 25.4 万 ms 压到 约 8600 ms,快了约 30 倍
  • 写入队列等候时间同步回落,数据不再堆积
高频 I/U/D 混合场景
第二个问题也被同一条路径解决。原本会被切成大量小 batch 的混合 I/U/D,先在内存里按主键压缩成最终状态,再用"聚集 DELETE + 聚集 MERGE"两条 SQL 一次性落库,省掉大量目标表反复探测和小事务提交开销。以 MySQL → OceanBase Oracle 实测为例:

同一套机制也适用于 Oracle 目标端。
混合 DML 如何收敛成两条聚集 SQL

对于同一张表里交错的 INSERT/UPDATE/DELETE,Staging Merge 会先在内存里按主键算出每个主键的最终状态,再收敛成两条聚集 SQL 一次性落库:需要删的主键走一条聚集 DELETE,需要写或更新的走一条聚集 MERGE。
压缩的关键不是简单取最后一行,而是要识别最后一次 INSERT 和 DELETE 的相对位置,保证结果正确:
原始变乱序列压缩后目标动作INSERT -> UPDATE -> UPDATE只写最终值一次UPDATE -> UPDATE -> DELETE只删除一次DELETE -> INSERT -> UPDATE删旧行后写最终值INSERT -> DELETE最终不保留适用场景与代价

收益

  • 高冲突批量写入性能更稳
  • 大幅减少服务端单行 MERGE 执行次数
  • 高频 I/U/D 交错时,多个小 batch 收敛为少量聚集 DML
  • 宽表场景比单行 MERGE 更友好
代价

  • 会在目标库的指定 schema 下自动创建临时表,任务删除时自动清算,无需手动管理
快速开始

如果你正在做 OceanBase for Oracle(或 Oracle)方向的数据同步,遇到类似的写入瓶颈,可以升级到 CloudCanal v6.3.0.0,开启 useStagingMerge。
老用户一键升级到 v6.3.0.0(Docker 托管镜像版):
  1. curl -fsSL https://tgzdownload.clougence.com/support/upgrade_on_docker.sh | bash -s -- 6.3.0.0 ./cc_home
复制代码
新用户一键安装(Docker 托管镜像版):
  1. curl -fsSL https://tgzdownload.clougence.com/support/install_on_docker.sh | bash -s -- 6.3.0.0 ./cc_home
复制代码
./cc_home 为安装目录,可按需调整。安装完成后浏览器访问 http://:8111 进入控制台。完整步骤见 安装文档 / 升级文档。
部署或使用中遇到任何问题,欢迎联系技术支持。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表