首批通过分布式安全可靠测评,为关键业务系统打造
保证 OceanBase 数据库高可用
更新时间:2026-04-14 17:41:09
ODP 除了其服务的高可用, 另一个重要特性就是帮助 OceanBase 数据库实现高可用特性,ODP 属于 OceanBase 数据库高可用体系中重要的一环。当 OceanBase 数据库出现问题后,一方面需要数据库系统及时恢复服务;另一方面需要 ODP 感知 OBServer 节点机器故障和服务变更。
ODP 要实现上述功能,主要通过故障探测、黑名单机制和 SQL 重试三种手段。故障探测用于发现故障节点,黑名单机制影响 ODP 的路由,SQL 重试保证 SQL 尽可能执行成功。
故障探测和黑名单机制
故障探测
在讲故障探测前,首先需要说明下故障的种类和探测难点。
对于故障的种类,我们主要分为以下两部分。
机器和进程故障:如机器宕机、网络故障、进程 core 等机器硬件问题或进程问题。
业务逻辑问题:指 OceanBase 数据库自身逻辑问题导致服务不可用,如 Paxos 选举失败导致无 LEADER 提供服务等。
目前,对于业务逻辑引发故障,与数据库的实现紧密相关,很难去确定问题边界并解决所有问题,比如业务逻辑出现死循环导致服务不可用,ODP 目前是无法解决这种问题的。
因此,ODP 的策略是在处理 OBServer 节点机器和进程故障的基础上,根据一些已知现象去处理一些业务逻辑问题,如 OBServer 节点返回的特定错误码(如机器内存不足)、OBServer 节点长时间无响应等。
在分布式系统中,故障探测的结果有三种:成功、失败和超时。其中,超时是最难处理的情况。比如一个 SQL 执行了 100s 仍然未返回结果,此时很难确定执行这个 SQL 的 OBServer 节点是否正常,有可能仅仅是 OBServer 节点执行慢,但也有可能确实是 OBServer 节点出现了问题;
另一方面,探测任务一般都是周期性的,在两次探测之间如果 OBServer 节点发生了状态变化,ODP 无法做到实时感知,因此可能会使用过期的信息去做一些路由选择,但这样就可能会导致一些慢 SQL 或者 SQL 执行失败。
故障探测的基础是节点状态定义,如定义健康和故障两种状态。但 OBServer 节点的状态要复杂很多,主要是因为将状态定义更加精细才能实现更多的高可用功能,让客户体验更好。ODP 对 OBServer 节点定义了如下八种状态。
ACTIVE状态:表示 OBServer 节点正常,可以提供服务。INACTIVE状态:表示 OBServer 节点异常,无法提供服务。UPGRADE状态:表示 OBServer 节点正在数据库版本升级阶段。REPLAY状态:表示 OBServer 节点在进行数据库日志的回放阶段。DELETING状态:表示 OBServer 节点正在被删除,此时可能正在进行数据迁移等操作。DELETED状态:表示节点已经被删除,不再属于该集群。DETECT_ALIVE状态:表示 ODP 对 OBServer 节点探测成功,认为 OBServer 节点状态正常。DETECT_DEAD状态:表示 ODP 对 OBServer 节点探测失败,认为 OBServer 节点状态异常。
虽然 OBServer 节点的状态很多,但是从原理上思考就会好记很多,如进程 core(INACTIVE 状态)、节点下线被删除(DELETING 或者 DELETED 状态)、新增节点(REPLAY 状态)、版本升级(UPGRADE 状态)、进程 hang 住(DETECT_DEAD 状态)。
其中前六个状态通过访问 OBServer 节点的视图可以获得,ODP 会周期性(20s/次)从 DBA_OB_SERVERS 和 DBA_OB_ZONES 视图中获取集群的机器状态信息,ODP 会通过 status、start_service_time、stop_time 定义 OBServer 节点状态。而 DETECT_ALIVE 和 DETECT_DEAD 状态则通过 ODP 的探测机制获得。
查询结果如下所示:
MySQL [oceanbase]> select * from DBA_OB_SERVERS\G
*************************** 1. row ***************************
SVR_IP: 10.10.10.2
SVR_PORT: 2882
ID: 1
ZONE: zone1
SQL_PORT: 2881
WITH_ROOTSERVER: YES
STATUS: ACTIVE
START_SERVICE_TIME: 2022-10-31 11:48:38.677315
STOP_TIME: NULL
BLOCK_MIGRATE_IN_TIME: NULL
CREATE_TIME: 2022-10-31 11:48:24.684250
MODIFY_TIME: 2022-10-31 11:48:39.682642
BUILD_VERSION: 4.0.0.0_100000252022102910-df01cef074936b9c9f177697500fad1dc304056f(Oct 29 2022 10:27:50)
MySQL [oceanbase]> select * from DBA_OB_ZONES\G
*************************** 1. row ***************************
ZONE: zone1
CREATE_TIME: 2022-10-31 11:48:29.040552
MODIFY_TIME: 2022-10-31 11:48:29.041609
STATUS: ACTIVE
IDC:
REGION: default_region
TYPE: ReadWrite
黑名单机制
需要注意的是,OBServer 节点发生状态变更后,ODP 会根据状态变更结果触发高可用相关的逻辑,许多内容和黑名单机制有关。
展开来讲,就是 ODP 探测出 OBServer 节点状态后,先修改黑名单,然后根据黑名单进行节点拉黑或节点洗白操作。根据不同的探测机制,ODP 实现了状态黑名单、探测黑名单和活不可用黑名单三种不同的黑名单。
状态黑名单
状态黑名单依赖于 OBServer 节点的状态变更。因为历史原因,和状态黑名单相关的状态不包含 DETECT_ALIVE 和 DETECT_DEAD 两种状态,剩下的状态变更都可以通过访问 OBServer 节点的视图获得。
当通过周期任务获得 OBServer 节点最新状态后,ODP 会进行根据 OBServer 节点状态的不同分别进行如下操作。
ACTIVE:将 OBServer 节点从状态黑名单中移除,洗白 OBServer 节点。INACTIVE/REPLAY:将 OBServer 节点加入状态黑名单。DELETED/DELETING状态:更新内存中 OBServer 节点的机器列表,不会向该节点转发 SQL。UPGRADE状态:不加入状态黑名单,但也不会向该节点转发 SQL,效果和黑名单类似。
探测黑名单
状态黑名单是从 OceanBase 的总控服务节点 RS 处获得信息,这种信息有时不能反映 ODP 和 OBServer 节点之间的情况。如下图所示, ODP 从 RS 处获得 OBServer1 节点状态为 ACTIVE,但 ODP 和 OBServer1 节点之间网络不通。

因此,ODP 在现有的状态黑名单基础上又实现了探测黑名单,通过给 OBServer 节点发送探测 SQL 来确定 OBServer 节点状态。ODP 会给 OceanBase 数据库的 sys 租户发送探测 SQL select 'detect server alive' from dual,并设置 5s 的超时时间,当超时时间内无结果返回时,会增加一次探测失败次数,当连续失败次数超过 3 次后认为探测失败,设置 OBServer 节点状态为 DETECT_DEAD。如果有结果返回,会将探测失败次数清零,设置 OBServer 节点状态为 DETECT_ALIVE。发生探测状态变更后,触发 ODP 行为如下。
DETECT_ALIVE状态:将 OBServer 节点从探测黑名单中洗白。DETECT_DEAD状态:将 OBServer 节点加入到探测黑名单,并将所有和该 OBServer 节点的连接关闭。
说明
如果将 OBServer 节点加入到探测黑名单后,不断开和该 OBServer 节点的连接,那么连接会被一直占用,无法发送新的 SQL,在性能数据上会看到这段时间的这台 OBServer 节点的 TPS 为 0。断开后,对于后续请求,ODP 会根据黑名单进行路由,不会转发给这台 OBServer 节点,TPS 就可以恢复。
活不可用黑名单
凭借状态黑名单和探测黑名单,ODP 已经可以很好地处理机器和进程故障,但 ODP 希望可以更近一步感知每个 SQL 执行失败的原因,去处理更复杂的情况,因此实现了活不可用黑名单。
活不可用黑名单机制的核心是根据业务 SQL 的执行结果去定义 OBServer 节点的状态,触发拉黑和洗白操作。相较于状态黑名单和探测黑名单,活不可用黑名单的拉黑和洗白谨慎了很多,主要是为了防止一次 SQL 执行结果产生误判。具体操作如下。
失败:根据时间点记录下一次失败事件。
ODP 向 OBServer 节点发送 SQL 后,超过 ob_query_timeout 时间后仍然无响应。
OBServer 节点返回错误码 OB_SERVER_IS_INIT、OB_SERVER_IS_STOPPING、OB_PACKET_CHECKSUM_ERROR、OB_ALLOCATE_MEMORY_FAILED。
ODP 和 OBServer 节点建连失败/报文解析失败,数据传输失败。
拉黑:OBServer 节点在 120s 内有五次活不可用的失败记录则拉黑这台 OBServer 节点。
尝试:加入活不可用黑名单超过 20s 后,尝试再次向该 OBServer 节点发送 SQL。
洗白:如果尝试的 SQL 执行成功则进行洗白。
可以看到,上文介绍的三种黑名单解决了不同维度的问题,少了任何一个都可能导致某种异常无法处理。上述三种黑名单之间并没有优先级,只要 OBServer 节点在任何一个黑名单中,这台机器就无法接收请求,换而言之,只有当 OBServer 节点不在任何黑名单中才会接收到请求。
普通代理一般只实现了探测机制对后端服务器做健康检查,但这种探测只能解决一些简单的问题。ODP 针对 OceanBase 数据库特性,做了更多的工作,如感知 OceanBase 数据库版本升级、日志回放、SQL 执行错误等。这是普通代理完全无法做到的。
查看黑名单
您可参考如下步骤查看黑名单。
在客户端中使用 root 用户,通过 ODP 代理登录集群的 sys 租户。
obclient -h10.10.10.1 -uroot@sys#obdemo -P2883 -p -c此处是以 ODP 机器 IP 为
10.10.10.1,集群名称为obdemo,连接端口为2883为例,通过 OBClient 连接集群,您需根据实际情况进行修改。执行如下命令查看黑名单信息。
SHOW PROXYCONGESTION [all] [clustername];相关参数说明如下:
不指定可选项时,可查询所有集群的黑名单信息。
仅指定
all时,可显示所有集群的 OBServer 节点信息(包括黑名单中和非黑名单中的 OBServer 节点信息)。仅指定
clustername时,可显示指定集群的黑名单信息,指定clustername时需使用英文单引号或双引号引起来。同时指定
all和clustername时,可显示指定集群的 OBServer 节点信息。
此处以查看单节点集群 obcluster 的黑名单信息为例。
show proxycongestion 'obcluster'\G
输出如下:
*************************** 1. row ***************************
cluster_name: obcluster
zone_name: zone1
region_name: sys_region
zone_state: ACTIVE
server_ip: xxx.xxx.xxx.xxx:2881
cr_version: 5
server_state: ACTIVE
alive_congested: 0
last_alive_congested: 0
dead_congested: 0
last_dead_congested: 0
stat_alive_failures: 0
stat_conn_failures: 0
conn_last_fail_time: 0
conn_failure_events: 0
alive_last_fail_time: 0
alive_failure_events: 0
ref_count: 2
detect_congested: 0
last_detect_congested: 0
说明
dead_congested和alive_congested两个字段分别对应黑名单和活不可用黑名单,这两个字段中任意一个状态为1时,表示该 OBServer 节点已被加入黑名单。如果机器被加入活不可用黑名单,ODP 在
congestion_retry_interval时间内不会转发任何请求给该机器,并在congestion_retry_interval时间后发起重试,并且该 OBServer 节点在min_keep_congestion_interval时间内不能被洗白。
SQL 重试
有了黑名单之后就可以实现 SQL 重试功能,SQL 重试的目的是让 SQL 完成执行,而不是简单地把错误抛给客户端。
可以进行重试的一个重要原则是不能产生预期外的行为。举个例子,用户执行了 SQL insert into t1 values (c1 + 1, 1),OBServer 节点很长一段时间没有响应,此时不能重新发给另一台 OBServer 节点,否则有可能导致 c1 变成了 c1 + 2,错误的重试危害更大。
ODP 的重试发生在两个阶段,一个是给 OBServer 节点转发前,一个是给 OBServer 节点转发后,下文将分别介绍这两个阶段。
转发前重试
本节以 ODP 架构图讲解转发前重试,下图中橙黄色部分即为转发前重试发生的位置。

以一个普通的 SQL select * from t 为例讲解转发前的重试逻辑。
假设 t 为分区表,表的 partition 的 LEADER 分布在 OBServer1 节点上,OBServer1 节点的 observer 进程 core 之后,RS 发现该机器 core 掉,设置节点状态为 INACTIVE。
ODP 会在数据路由阶段选择 OBServer1 节点,之后进行容灾管理检查,发现 OBServer1 节点在状态黑名单中,此时就需要进行路由重试。通过 租户内路由,从租户的机器列表中选择机器路由,然后再进行容灾检查,直到容灾检查通过才进行转发。
和所有故障一样,越早发现问题并解决效率越高,转发前重试只发生在 ODP 内部,快速的处理了问题,业务几乎无感知。
转发后重试
转发前重试很高效,但依赖黑名单的准确性,在分布式系统中,ODP 无法实时感知机器状态,因此在进行转发前,可能并不知道机器发生了问题。此时根据转发的结果可以进行一定程度的重试。
转发的常见结果有两种
OBServer 节点机器故障导致 TCP 连接失败
如果无特殊限制(如事务路由),此时可以选择新的机器进行重试。
OBServer 节点执行失败返回错误码
因为错误码太过于细节本文就不作详细介绍,可以参考 ODP 的开源代码
ObMysqlTransact::handle_oceanbase_server_resp_error去梳理。举例说明, 如果 OBServer 节点返回错误码REQUEST_SERVER_INIT_ERROR,说明 OBServer 节点初始化失败,则可以进行重新转发,后续逻辑和转发前重试逻辑相似。
通过转发前重试和转发后重试,ODP 处理了大量的 OBServer 节点问题,整个过程对用户完全透明,用户可能都不知道发生过故障。其中,转发前重试效率高,但依赖黑名单机制;转发后重试是转发前重试一种补充,尽可能地屏蔽后端异常情况。
总之,故障探测、黑名单和 SQL 重试是 ODP 高可用特性非常重要的三个功能,三者也有一定的关系。比如转发后重试和活不可用黑名单都依赖 OBServer 节点的反馈信息,转发前重试依赖黑名单机制。
通过上面三个高可用功能, ODP 处理了大部分的 OBServer 节点异常,如网络故障、observer 进程异常、机器宕机等;同时,ODP 也进一步识别 OceanBase 数据库本身的一些逻辑异常,如版本升级、日志回放等,让 OceanBase 数据库做的更加的好用。
和 OceanBase 高可用关系
OceanBase 数据库的高可用和 ODP 不同,OceanBase 数据库基于 Paxos 算法,保证在多数派正常情况下就可以正常提供服务,容忍了少数派节点异常。但 OceanBase 数据库的高可用可能会导致服务节点从 A 切换到 B(切主操作),因此需要 ODP 更快更准的找到正常服务节点。所以两者之间相互配合,对用户提供稳定的服务。
更多 OceanBase 数据库高可用介绍,请参考 OceanBase 数据库文档 高可用 章节。