×
技术社区 >  技术博客 >  OceanBase 读写分离配置指南(SOP+回滚)

OceanBase 读写分离配置指南(SOP+回滚)

版本要求

  • OBProxy (建议 ≥ 4.3.2)

适用场景

单 OBProxy 入口、事务内强读走 Leader、事务外只读走弱读并路由到 Follower。

一、背景与原理

1.1 读写分离机制

OB 通过弱一致性读将只读请求路由到 Follower 副本,写请求始终走 Leader 强一致性。

ODP 提供两种自动读写分离模式:

模式 参数 行为 推荐度
基础读写分离 enable_read_write_split=true 仅将事务外的读请求(包括 autocommit=1 的单条查询)自动视为弱读并路由到副本。一旦进入显式事务(如执行了 BEGINSTART TRANSACTION),后续所有读写请求均发往主库。 推荐
事务拆分读写分离 enable_transaction_split=true 在显式事务内部,如果读请求位于 DML 语句之前,ODP 也会将其视为弱读并路由到副本 不推荐

基础读写分离严格保证了事务内的数据强一致性,避免了因主备延迟导致的「读不到自己刚写入的数据」等问题,兼顾了性能与安全,是生产环境的推荐选择。

事务拆分读写分离存在数据一致性风险。由于副本同步可能存在延迟,事务内读取到的可能是旧数据,导致业务逻辑基于过期数据进行判断或计算,进而引发数据错误。仅在能容忍短暂不一致且对事务内读性能有极端要求的特殊场景下才考虑使用。

1.2 什么叫事务外读和事务内读

1.2.1 事务外读

  • 没有 BEGIN
  • 连接默认 autocommit=1
  • 单独执行一条普通 SELECT

例如:

SELECT * FROM order_table WHERE id = 100;

这种查询通常是:

  • 页面查列表
  • 报表统计
  • 单独查详情

它和后面的写没有绑定关系,所以可以让它走 Follower。

1.2.2 事务内读

就是:

  • 已经 BEGIN
  • 或者连接是 autocommit=0
  • 这个 SELECT 和后面的 UPDATE 属于同一个业务动作
BEGIN;
SELECT balance FROM account WHERE id = 1;
UPDATE account SET balance = balance - 100 WHERE id = 1;
COMMIT;

这是一个完整业务:

  1. 先查余额
  2. 再扣钱

这时第一条 SELECT 不能随便走 Follower,因为:

  • Follower 上的余额可能还是旧值
  • 后面 UPDATE 却在 Leader 上按旧值计算
  • 就可能出现业务不一致

所以事务里的读,必须走强读,去 Leader。

1.3 proxy_route_policy=FOLLOWER_FIRST

前面说的是:这条 SQL 是强读还是弱读。

这个参数说的是:如果是弱读,优先发给谁?

FOLLOWER_FIRST 的意思是:弱读请求优先路由到 Follower,只有备库不可用时,才回退到 Leader。

所以 proxy_route_policyenable_read_write_split 是配合关系:

  • enable_read_write_split:决定「哪些读可以弱读」
  • proxy_route_policy:决定「弱读优先找谁」。只对「已经被判定为弱读」的 SQL 生效。

比如这个环境:

  • zone1:Follower
  • zone2:Leader
  • zone3:Follower

一条弱读 SQL 来了以后,理论上可以发给:

  • zone1 的 Follower
  • zone3 的 Follower
  • 实在不行才发给 zone2 的 Leader

FOLLOWER_FIRST 就是在说:先在 Follower 里挑,不要一上来就选 Leader。

proxy_route_policy=FOLLOWER_FIRST 时,路由优先级为:

> Region(odp 与 observer 同 region)> 主备角色(先 follower 后 leader)> 合并状态(弱读优先选不在合并中的副本)> IDC(odp 优先选择同机房的副本)

1.4 为什么还要配 ob_read_consistency=strong

这个参数是 OB 租户级默认读一致性。

这里容易误会:既然要做读写分离,为什么还把 ob_read_consistency 设成 strong

原因是:

  • 租户默认保持强读
  • 这样事务内读、写相关读仍然安全
  • 只有 OBProxy 判定为「事务外普通查询」时,才自动改成弱读

也就是说:不是把整个租户都改成弱读,而是默认强读,只对符合条件的查询自动弱读。

二、方案选型

方案 A:基础读写分离(推荐)

适用于:业务通过同一 OBProxy 接入,事务内强读、事务外自动弱读。

-- OBProxy 配置
ALTER PROXYCONFIG SET enable_read_write_split = true;
ALTER PROXYCONFIG SET proxy_route_policy = 'FOLLOWER_FIRST';

无需额外设置弱读参数;开启读写分离后,OBProxy 自动将事务外读请求视为弱读。

方案 B:事务拆分读写分离(不推荐)

ALTER PROXYCONFIG SET enable_transaction_split = true;
ALTER PROXYCONFIG SET proxy_route_policy = 'FOLLOWER_FIRST';

-- OB 内核参数
ALTER SYSTEM SET ob_proxy_readonly_transaction_routing_policy = false TENANT = 'tenant_name';

风险: 事务内、DML 之前的读也会走 Follower,可能出现数据不一致,生产环境不建议开启。

三、生产推荐配置

3.1 架构说明

项目 配置
副本拓扑 2F1A(2 个 FULL Follower + 1 个 FULL Leader)
Primary Zone zone1(Leader 所在 Zone)
弱读目标 Zone zone2(Follower)
接入方式 同一 OBProxy 节点处理读写分离

预期行为:

  • 事务内读写 → 强读,路由 zone1(Leader)
  • 事务外只读 → 弱读,路由 zone2(Follower)

3.2 OBProxy 读写分析配置

-- ① 开启基础读写分离,事务外普通 select 自动当弱读,写、事务内读、select for update 仍然走强读
ALTER PROXYCONFIG SET enable_read_write_split = true;

-- ② 备优先读路由策略。当一条 SQL 已经被判定为弱读后,决定优先发给谁。
--    FOLLOWER_FIRST = 弱读优先找 Follower,备库不行才回退 Leader
ALTER PROXYCONFIG SET proxy_route_policy = 'FOLLOWER_FIRST';

-- ③ 关闭连接缓存(SQL 无法解析时避免复用 Leader 连接)。
--    正常情况下,OBProxy 解析 SQL 后,知道该连哪台 OBServer。但有些复杂 SQL 解析不出表名时,OBProxy 会走兜底逻辑:
ALTER PROXYCONFIG SET enable_cached_server = false;

-- ④ 关闭 Primary Zone 路由(避免登录时固定到 Leader)
ALTER PROXYCONFIG SET enable_primary_zone = false;

-- ⑤ 代理层保持强读默认(由读写分离逻辑自动改写事务外读为弱读)
ALTER PROXYCONFIG SET obproxy_read_consistency = 0;

-- ⑥ LDC:将 proxy_idc_name 设置为弱读 Zone 的 IDC
ALTER PROXYCONFIG SET proxy_idc_name = 'IDC2';   -- 对应 zone2

3.3 OBServer / 租户读写分离配置

-- 租户变量:保持强读默认
ALTER TENANT tenant_name SET VARIABLES ob_read_consistency = 'STRONG';

-- 内核参数:事务内 BEGIN 后的只读语句视为事务内读(不走弱读拆分)
ALTER SYSTEM SET ob_proxy_readonly_transaction_routing_policy = false TENANT = 'tenant_name';

3.4 Zone LDC 配置

-- zone1(Leader Zone)
ALTER SYSTEM MODIFY ZONE zone1 SET REGION = 'Region1';
ALTER SYSTEM MODIFY ZONE zone1 SET IDC = 'IDC1';

-- zone2(弱读目标 Zone)
ALTER SYSTEM MODIFY ZONE zone2 SET REGION = 'Region2';
ALTER SYSTEM MODIFY ZONE zone2 SET IDC = 'IDC2';

-- 验证
SELECT zone, name, info FROM __all_zone WHERE name IN ('region', 'idc');
SHOW PROXYINFO IDC;

3.5 弱读 Zone IDC 对齐

当存在复杂 SQL 无法解析表名时,OBProxy 无法判断弱读副本位置,会将所有 Zone 视为 Follower 并随机路由。

弱读路由兜底优先级:

  1. Last Session(上次连接)
  2. IDC 路由

处理方式(二选一):

方式 1:统一 IDC 指向弱读 Zone(传统方案)

-- 所有 OBProxy 节点的 proxy_idc_name 均设为弱读 Zone 的 IDC
ALTER PROXYCONFIG SET proxy_idc_name = 'IDC2';
ALTER PROXYCONFIG SET enable_cached_server = false;  -- 必须关闭

方式 2:指定副本类型路由(OBProxy ≥ 4.3.2,推荐)

ALTER PROXYCONFIG SET route_target_replica_type = 'Full;Readonly';
-- 无需再手动对齐 region/idc

3.6 二次路由优化(建议开启)

避免强读 SQL 因复制表等同 IDC 路由误发到 Follower,产生大量 Remote 计划:

ALTER PROXYCONFIG SET enable_ob_protocol_v2 = true;   -- 默认 True
ALTER PROXYCONFIG SET enable_reroute = true;         -- 默认 False,建议开启
ALTER PROXYCONFIG SET enable_index_route = true;     -- 默认 False,建议开启

四、测试环境配置示例

4.1 环境

节点 IP Region IDC 角色
zone1 172.20.5.171 Region1 IDC1 Follower
zone2 172.20.5.170 Region2 IDC2 Leader
zone3 172.20.5.175 Region1 IDC1 Follower
OBProxy - - IDC1 代理
-- 租户 Primary Zone
ALTER TENANT tenant_name PRIMARY_ZONE = 'zone2;zone1,zone3';

4.2 完整配置

-- ========== OBProxy ==========
ALTER PROXYCONFIG SET enable_read_write_split = true;
ALTER PROXYCONFIG SET proxy_route_policy = 'FOLLOWER_FIRST';
ALTER PROXYCONFIG SET enable_cached_server = false;
ALTER PROXYCONFIG SET enable_primary_zone = false;
ALTER PROXYCONFIG SET obproxy_read_consistency = 0;
ALTER PROXYCONFIG SET proxy_idc_name = 'IDC2';  -- 弱读 Zone 的 IDC

-- ========== OB 租户 ==========
ALTER TENANT tenant_name SET VARIABLES ob_read_consistency = 'STRONG';
ALTER SYSTEM SET ob_proxy_readonly_transaction_routing_policy = false TENANT = 'tenant_name';

五、配置变更 SOP 操作流程

Step 1:变更前检查

-- 1. 确认副本分布与 Leader 位置
SELECT svr_ip, role, COUNT(*) 
FROM oceanbase.DBA_OB_TABLE_LOCATIONS 
WHERE tenant_id = 
GROUP BY svr_ip, role;

-- 2. 确认 Primary Zone
SELECT tenant_name, primary_zone FROM oceanbase.DBA_OB_TENANTS;

-- 3. 确认当前 OBProxy 配置
SHOW PROXYCONFIG LIKE '%read_write%';
SHOW PROXYCONFIG LIKE '%route_policy%';
SHOW PROXYCONFIG LIKE '%cached_server%';
SHOW PROXYCONFIG LIKE '%proxy_idc%';
SHOW PROXYINFO IDC;

-- 4. 确认 Zone LDC
SELECT zone, name, info FROM oceanbase.__all_zone WHERE name IN ('region', 'idc');

Step 2:配置变更

-- 按第三节顺序逐项配置
-- 建议:先在测试环境验证,再单 OBProxy 节点灰度,最后全量

Step 3:变更后验证

-- 1. 事务外弱读验证
SELECT /*+READ_CONSISTENCY(WEAK)*/ * FROM test_table LIMIT 1;

-- 2. 查看 SQL 审计中的 consistency_level
SELECT trace_id, svr_ip, consistency_level, query_sql 
FROM oceanbase.GV$OB_SQL_AUDIT 
WHERE tenant_id = 
ORDER BY request_time DESC LIMIT 20;

-- 3. 确认弱读落到 Follower 节点
-- consistency_level = 2 表示弱读
-- svr_ip 应为 Follower 所在 IP(如 172.20.5.171 / 172.20.5.175)

-- 4. 事务内强读验证
BEGIN;
SELECT * FROM test_table LIMIT 1;  -- 应路由到 Leader (172.20.5.170)
COMMIT;

-- 5. 检查 Remote 计划比例(应较低)
SELECT plan_type, COUNT(*) 
FROM oceanbase.GV$OB_SQL_AUDIT 
WHERE tenant_id =  AND request_time > DATE_SUB(NOW(), INTERVAL 1 HOUR)
GROUP BY plan_type;

Step 4:监控观察

监控项 预期 异常处理
Leader CPU/内存 事务负载集中 正常
Follower CPU/内存 报表/查询负载集中 正常
Remote 计划比例 < 5% 检查 enable_reroute、IDC 配置
弱读延迟 < max_stale_time_for_weak_consistency(默认 5s) 检查副本同步
主副本误接收弱读流量 应为 0 检查 enable_cached_server

六、参数速查

参数 层级 推荐值 说明
enable_read_write_split OBProxy true 开启基础读写分离
enable_transaction_split OBProxy false 不开启事务拆分
proxy_route_policy OBProxy FOLLOWER_FIRST 备优先读
enable_cached_server OBProxy false 关闭连接缓存,避免解析失败时粘 Leader
enable_primary_zone OBProxy false 避免登录固定到 Leader
obproxy_read_consistency OBProxy 0 0=强读(由读写分离自动改写)
proxy_idc_name OBProxy 弱读 Zone 的 IDC LDC 就近路由
route_target_replica_type OBProxy Full;Readonly 4.3.2+ 可替代 IDC 对齐
ob_read_consistency 租户 STRONG 默认强读,事务内保持强读
ob_proxy_readonly_transaction_routing_policy 租户 false BEGIN 后只读视为事务内读

七、常见问题与排查

Q1:只读地址流量仍打到 Leader

原因: enable_cached_server=trueenable_primary_zone=true 时,登录连到 Leader 后,无法解析的 SQL 会复用该连接。

处理:

ALTER PROXYCONFIG SET enable_cached_server = false;
ALTER PROXYCONFIG SET enable_primary_zone = false;

Q2:复杂 SQL 弱读随机路由到 Leader

原因: OBProxy 无法解析表名,所有 Zone 被视为 Follower,随机路由。

处理:

  • 设置 proxy_idc_name 为弱读 Zone 的 IDC
  • 设置 enable_cached_server=false
  • 或 OBProxy ≥ 4.3.2 使用 route_target_replica_type='Full;Readonly'

Q3:复制表强读也路由到 Follower

原因: proxy_idc_name 与 Follower 同 IDC,复制表走路由 same_idc 策略。

处理: 开启 enable_reroute=true,并观察 Remote 计划比例。

Q4:用友 BIP 生产是否可用 enable_read_write_split

BIP 生产优先在应用层通过 /*+READ_CONSISTENCY(WEAK)*/ Hint 显式指定弱读,而非依赖 OBProxy 自动拆分。若必须使用自动拆分,需充分测试复杂 SQL 的路由行为,并接受弱读一致性风险。

八、回滚方案

-- 关闭读写分离,恢复默认路由
ALTER PROXYCONFIG SET enable_read_write_split = false;
ALTER PROXYCONFIG SET proxy_route_policy = '';
ALTER PROXYCONFIG SET enable_cached_server = true;
ALTER PROXYCONFIG SET enable_primary_zone = true;

-- 验证所有请求回到 Leader
SELECT svr_ip, role, COUNT(*) 
FROM oceanbase.GV$OB_SQL_AUDIT 
WHERE tenant_id =  AND request_time > DATE_SUB(NOW(), INTERVAL 10 MINUTE)
GROUP BY svr_ip;

精选推荐