数据库代理
数据库代理
代理概述
OceanBase Database Proxy(简称 ODP )是 OceanBase 数据库专用的代理服务器。OceanBase 数据库的用户数据以多副本的形式存放在各个 OBServer 节点上,ODP 接收用户发出的 SQL 请求,并将 SQL 请求转发至最佳目标 OBServer,最后将执行结果返回给用户。
为什么需要 ODP?
ODP 是代理服务器,代理服务器会让访问数据库的链路多一次跳转,那为什么需要 ODP 呢?我们以下图为例进行说明。

图中 APP 是我们的业务程序,APP 前面有三台 ODP( ODP 的进程名叫做 obproxy ),在实际部署中,ODP 和 APP 之间一般会有一个负载均衡(如:F5 )将请求分散到多台 ODP 上面,ODP 下面是 OBServer 节点,图中有 6 个 OBServer 节点。
需要使用 ODP 的原因如下:
-
连接管理:OceanBase 集群规模庞大,机器、软件出现问题或者本身运维机器上线、下线概率较大,如果直连 OBServer 节点,遇到上面的情况客户端就会发生断连。ODP 屏蔽了 OBServer 节点本身分布式的复杂性,客户连接 ODP,ODP 可以保证连接的稳定性,自身对 OBServer 节点的复杂状态进行处理。
-
数据路由:ODP 可以获取到 OBServer 节点中的数据分布信息,可以将用户 SQL 高效转发到数据所在机器,执行效率更高。如:表 t1 数据在图中 P1 内,表 t2 数据在图中 P2 内,表 t3 数据在图中 P3 内。对于 insert into t1 语句 ODP 可以将 SQL 转发到 ZONE_2 中含有 P1 主副本的机器上。对于 update t2 语句 ODP 可以将 SQL 转发到 ZONE_1 中含有 P2 主副本的机器上。
ODP 特性
作为 OceanBase 数据库的关键组件,ODP 具有如下特性:
-
高性能转发
ODP 完整兼容 MySQL 协议,并支持 OceanBase 自研协议,采用多线程异步框架和透明流式转发的设计,保证了数据的高性能转发,同时确保了自身对机器资源的最小消耗。 -
最佳路由
ODP 会充分考虑用户请求涉及的副本位置、用户配置的读写分离路由策略、OceanBase 数据库多地部署的最优链路,以及 OceanBase 数据库各机器的状态及负载情况,将用户的请求路由到最佳的 OBServer 节点上,最大程度的保证了 OceanBase 数据库整体的高性能运转。 -
连接管理
针对一个客户端的物理连接,ODP 维持自身到后端多个 OBServer 节点的连接,采用基于版本号的增量同步方案维持了每个 OBServer 节点连接的会话状态,保证了客户端高效访问各个 OBServer 节点。 -
专有协议
ODP 与 OBServer 节点默认采用了 OceanBase 专有协议,如增加报文的 CRC 校验保证与 OBServer 节点链路的正确性,增强传输协议以支持 Oracle 兼容性的数据类型和交互模型。 -
安全可信
ODP 支持使用 SSL 访问数据,并和 MySQL 协议做了兼容,满足客户安全需求。 -
易运维
ODP 本身无状态支持无限水平扩展,支持同时访问多个 OceanBase 集群。可以通过丰富的内部命令实现对自身状态的实时监控,提供极大的运维便利性。
许可证
ODP 社区版完全开源,使用 MulanPubL - 2.0 许可证,您可以免费复制和使用源代码。当您修改或分发源代码时,请遵守木兰协议
SQL 路由
ODP 会充分考虑用户请求涉及的副本位置、用户配置的读写分离路由策略、OceanBase 数据库多地部署的最优链路,以及 OceanBase 数据库各机器的状态及负载情况,将用户的请求路由到最佳的 OBServer 节点,最大程度的保证了 OceanBase 数据库整体的高性能运转。
开始阅读本节内容之前,您可以先了解一些路由相关的概念,便于您更好的了解下面的内容。
- Zone
- Region
- Server List
- RS List
- Location Cache
- 副本
- 合并
- 强一致性读/弱一致性读
- 读写 Zone
- 分区表
- 分区键
OceanBase 数据库执行计划
执行计划分为三种:Local 、Remote 、Distribute。ODP 的作用主要是尽量避免 Remote 计划(效率低,性能差),将路由尽可能准确变为 Local 计划。
ODP 路由作用
理解了上述 可用区/Region/分区/副本的基本概念和物理含义后,就能理解 ODP 的路由思路了。从分区的设计到其物理分布,以及本地执行计划的高效性考虑,ODP 需要尽可能对 SQL 进行准确路由,主要过程将经历:SQL 解析、分区计算、分区信息获取、副本策略选择等。
非分区表路由
非分区表可以直接利用 Location Cache 中的副本信息。ODP 保存了分区和 OBServer 节点地址的映射,通过解析 SQL 中的表名,根据表名查询 ODP 缓存中的分区对应的机器 IP。缓存的有效性,有以下三种情况:
- 缓存中找不到,此时需要访问 OBServer 节点查询最新映射并缓存。
- 缓存中存在但不可用,此时需要重新去 OBServer 节点查询并更新。
- 缓存中存在且可用,此时可以直接使用。
分区表路由
分区表的路由相比非分区表而言,增加了分区 ID 及其相关的计算和查询过程。在获取了 Location Cache 后,分区表需要继续判定表的一级/二级分区,根据不同分区键类型和计算方式,计算分区 ID 并获取对应主备副本信息。
做分区计算时,通过表结构可以得知分区键及其类型,之后通过解析 SQL 语句获取对应分区键的值,并根据表结构和分区键类型做分区计算,从而能转发到对应分区所在的机器。
正常情况下,通过分区计算,ODP 可以将 SQL 路由到分区对应的机器上,从而避免 Remote 执行,提升效率。在 ODP V3.2.0 版本,已针对分区表且无法计算分区路由的场景进行优化,由随机选择租户的机器路由,优化成随机从分布了分区的机器中随机路由,提升命中率,尽可能减少 Remote 执行。
副本路由选择(常规部署)
对于强一致性读,且 SQL 指定了表名,将路由到该表对应分区的 Leader 的 OBServer 节点;对于弱一致性读、登录认证请求、强一致性读但没有指定表名等情况,存在主备均衡路由(默认)、备优先路由、不合并备优先路由这三种路由策略。
主备均衡路由(默认)
将按照以下优先级进行路由选择:
- 相同 Region,相同 IDC,不在合并状态的节点。
- 相同 Region,不同 IDC,不在合并状态的节点。
- 相同 Region,相同 IDC,正在合并状态的节点。
- 相同 Region,不同 IDC,正在合并状态的节点。
- 不同 Region,不在合并状态的节点。
- 不同 Region,正在合并状态的节点。
备优先路由
常规部署下,支持备优先读策略。通过用户级别的系统变量 proxy_route_policy 控制,仅在常规部署和弱一致性读的情况下生效,优先读 Follower 而非主备均衡路由。
在常规模式部署和弱一致性读时,设置 set @proxy_route_policy='follower_first';,路由将优先发往备节点,即使 OBServer 节点处于正在合并状态。将按照以下优先级进行路由选择:
- 相同 Region,相同 IDC,不在合并状态的备节点。
- 相同 Region,不同 IDC,不在合并状态的备节点。
- 相同 Region,相同 IDC,正在合并状态的备节点。
- 相同 Region,不同 IDC,正在合并状态的备节点。
- 相同 Region,相同 IDC,不在合并状态的主节点。
- 相同 Region,不同 IDC,不在合并状态的主节点。
- 不同 Region,不在合并状态的备节点。
- 不同 Region,正在合并状态的备节点。
- 不同 Region,不在合并状态的主节点。
- 不同 Region,正在合并状态的主节点。
强制发送到只读副本的路由
常规部署下,弱一致性读,设置 set @proxy_route_policy='FORCE_READONLY_ZONE'; 时,路由将所有的只读请求不会转发到非只读副本。其规则如下:
- 弱读请求:路由到只读副本中的节点。
- 非弱读请求:报错 4007,该路由规则下不允许非只读请求,如果业务确实需要发送非只读请求,请将变量 ob_route_policy 的值设置为其他的规则。
其他
常规部署下,弱一致性读,当 proxy_route_policy 变量取其他值时,路由选择将退化为普通的弱一致性读主备均衡路由策略,即主备均衡路由(默认)。
副本路由选择(读写分离部署)
即使用了只读副本的部署模式。读写分离部署时,不存在备优先读的路由策略,路由依赖系统变量 ob_route_policy。主要存在以下几种情况:
1.对于强一致性读语句,且 SQL 指定了表名,那么直接路由到该表涉及的分区的 Leader 的 OBServer 节点执行。
2.对于强一致性读语句,SQL 为"select 不指定表名/use database/set session 级别系统变量"时,忽略 Zone 属性,此时等同于常规部署下的主备均衡路由(默认)。
3.对于强一致性读语句,不包含登录认证请求,且去除 1 & 2 场景的请求,将按照以下策略进行路由:
- 相同 Region,相同 IDC,不在合并状态的读写 Zone 中的节点。
- 相同 Region,不同 IDC,不在合并状态的读写 Zone 中的节点。
- 相同 Region,相同 IDC,正在合并状态的读写 Zone 中的节点。
- 相同 Region,不同 IDC,正在合并状态的读写 Zone 中的节点。
- 不同 Region,不在合并状态的读写 Zone 中的节点。
- 不同 Region,正在合并状态的读写 Zone 中的节点。
连接管理
针对一个客户端的物理连接,ODP 维持自身到后端多个 OBServer 节点的连接,采用基于版本号的增量同步方案维持每个 OBServer 节点的连接在同一状态,保证了客户端高效访问各个 OBServer 节点。连接管理的另外一个功能是连接保持,在 OBServer 节点宕机/升级/重启时,客户端与 ODP 的连接不会断开,ODP 可以迅速切换到健康的 OBServer 节点上,对应用透明。
创建连接
ODP 的 Session 分为两类:Client Session 和 Server Session。Client Session 指的是客户端与 ODP 之间建立的连接;Server Session 指的是 ODP 与 OBServer 节点之间创建的连接。当 ODP 向 OBServer 节点转发客户端请求时,如果与 OBServer 节点之间没有创建连接,则需要初始化一个 Session 实例。
创建 Session 时需要做认证操作,ODP 无法决定该 Session 将来要访问哪些 OBServer,所以认证过程中,ODP 只能任意选择一个 OBServer 节点进行认证,将客户端发送的认证数据包转发给 OBServer 节点,并将 OBServer 节点返回的结果转发给客户端,同时将客户端认证的数据包都缓存在 Client Session 内部,以便 ODP 与其它 OBServer 节点建立该 Client Session 关联的 Server Session 时,将这些认证数据包发送给 OBServer 节点,便于与 OBServer 节点顺利进行认证。
存储连接
每当客户端向 ODP 发送请求,需要根据客户端连接信息查询获取 Client Session。在非连接池模式下,Server Session 不能脱离 Client Session 独立存在,只有根据 Client Session 来查询其关联的 Server Session,或者查询某个 Server Session 是否是与某个 Client Session 关联。
ODP 接收到客户端 SQL 请求时,会通过查询 partition table cache 来获取到 OBServer 节点的地址,然后查询 Client Session 保存的 Server Session 有没有与该 OBServer 节点关联的 Server Session 存在,如果存在则使用该 Server Session,如果不存在,则与该 OBServer 节点创建连接,并将 Server Session 加入 Client Session 关联的 Server Session 存储结构中。
事务状态维护
ODP 以事务来绑定 Client Session 与 Server Session。事务的第一条语句到达 ODP 时,ODP 选择一个 Server Session,并绑定到 Client Session 上,以后在整个事务处理过程中,都使用该 Server Session 转发数据到 OBServer,在事务中不能切换 Server Session,所以需要记录事务的状态到 Client Session中。
在事务开启时,记录事务开启状态到 Client Session,事务结束时将该事务状态重置。那么怎么来判断事务是否结束了呢?如果使用 autocommit,每个请求都要解析 OBServer 节点的返回包,如果一旦数据转发完成,就将该请求的事务状态重置。如果是长事务,只需要在 commit/rollback 请求后解析 OBServer 节点数据包来判断事务是否结束。
维护事务状态的目的主要是保证在事务中不切换 Server Session,也就是说,如果在事务中,OBServer 宕机,ODP 需要感知 OBServer 节点状态,并将未完结的事务结束掉。因为 MySQL 协议没有超时机制,ODP 必须主动通知客户端事务已经终止。OBServer 节点也没有对事务做同步,暂时也不能实现事务迁移功能,也就是说事务只能由接受请求的那个 OBServer 负责响应结果,换一台 OBServer 就不能处理,基于这个原因,在 ODP 做主备切换时,一定要保证正在处理的事务完成后才能切换,或者返回特殊错误,便于 ODP 通知客户端终止事务。
维护事务状态还有一个目的就是在 ODP 升级时,旧 ODP 经历一段时间,活跃的 Client Session 比较少时,采用 Kill 旧 ODP 的方式停止服务,停止服务也要保证在所有事务完结后才能进行,尽量减小对用户的影响。
连接变量管理
Client Session 需要记录客户端在该 Session 上所有设置过的变量,每次修改一个变量,在 Client Session 中记下修改时间。当 ODP 选择一个 Server Session 转发请求时,首先检查该 Server Session 上 Session 变量的修改时间是否大于 Client Session 中记录的修改时间。若小于,则意味着该 Session 没有使用最新的 Session 变量。ODP 会先重置该 Server Session 上的所有 Session 变量,批量将当前 Client Session 中保存的 Session 变量设置到该 Server Session 上后,再通过该 Server Session 转发请求;反之,则直接通过 Server Session 转发。
对于一些常见的 Session 变量,如:autocommit 等,客户端可能频繁设置,所以不能使用根据 modify time 来决定是否批量重置,而是每个 Server Session 存储这些常见 Session 变量的值,每次请求都对比 Client Session 中的变量值与 Server Session 中的变量值是否一致,不一致则重新设置这些变量值。
闪断避免
闪断避免指的是 ODP 与 OBServer 节点的 Server Session 异常不会被客户端感知,客户端与 ODP 之间的 Client Session 正常,客户端能够正常的读写数据。需要处理 Server Session 异常的情况如下:
-
OBServer 节点发生 Leader 切换,ODP 还没有获取到新的 Leader。这种情况主要由 OBServer 节点来处理,Leader 切换一定要保证将正在处理的事务都完成,ODP 只是尽力保证将请求发送给数据所在的 OBServer 节点,OBServer 节点发生了 Leader 切换,那么即使数据不在该 OBServer 节点上,该节点也需要负责处理该请求,并把结果返回给 ODP。
-
OBServer 节点发生宕机,那么 ODP 与该 OBServer 节点的连接就会断开。如果该 Server Session 正在处理事务中,那么 ODP 需要发送一个错误响应给客户端;如果该 Server Session 处于空闲状态,只需将该 Server Session 从其对应的 Client Session 中标记删除即可。新的请求将不再使用该 Server Session 转发请求。
-
ODP 与 OBServer 节点通信超时,MySQL 协议本没有超时机制,但 OBServer 节点有超时机制,所以 OBserver 节点超时会通知 ODP,ODP 将错误通知给客户端。如果 OBServer 节点发现 Server Session 长时间不活动,也会 Kill 该 Session,这种情况的处理参考第 2 项的处理方式。
-
ODP 与 OBServer 节点之间的网络连接断开或发生网络分区,这种情况处理参考第 2 项,即使发生网络分区,如果 Server Session 上有事务正在执行,OBServer 在一段时间后终止该 Session,所以 ODP 在 Server Session 断开一段时间后,给客户端报错,结束未完成的事务,避免 Session 长时间被挂住。
-
ODP 升级,新启动的 ODP 将负责客户端发起的新 Session,旧 ODP 上的 Session 数量会越来越少,当旧 ODP 上的 Session 数量低于某个阈值时,需要将旧 ODP 上的 Session 全部都终止掉(当然要保证正在处理的事务完成,长事务需要等一段时间,超时仍然没有完成也只能强制 Kill 掉),然后停止旧 ODP。这个过程无法完全避免客户端连接闪断。
-
ODP 宕机,这种情况下,ODP 上的连接都会断掉,可能很快 ODP 被重新启动,或者该 ODP 负责的连接被其它的 ODP 处理,闪断无法避免。
使用 ODP 能够避免大部分异常情况下的连接闪断,特别是在 OBServer 节点发生 Leader 切换、主备集群切换、ODP 升级等后端维护的情况下,能保持客户端连接正常,对客户端的影响比较小。
Kill Session 处理
MySQL 协议没有超时机制,但 MySQL 会 Kill 长时间不活跃的 Session,如果客户端一直等不到服务端的响应,会一直等待,遇到这种挂住的情况,一般 DBA 会介入,调用 MySQL 的 Kill Session 命令将 Session 关闭。
OBServer 节点有超时机制,一般情况下,无论是服务端宕机,还是网络分区,或者 obproxy 与服务端的连接断开,obproxy 都能够通知 Client Session 事务处理失败。但如果超时时间设置得过长,或者 obproxy 处理有遗漏,Client Session 确实挂住了,那么应用就会要求做 Kill Session 操作。obproxy 需要能够对 Kill Session 做特殊处理,不仅要处理 obproxy 上记录的 Client Session 状态,还需要通知 OBServer关闭 Server Session。
ODP 配置管理
ODP 的配置控制着 ODP 的行为,您可以修改 ODP 的配置、查看配置等。
ODP 会将配置保存到本地文件中,该文件放在 obproxy 启动目录的 etc 下,名称为 obproxy_config.bin,查看该文件也可以看到简单的配置内容,但该文件不能修改,否则会导致配置异常。
目前 ODP 的配置都是进程级别,设置后影响所有访问 ODP 的请求,无法进行租户、集群、用户等细粒度配置。
参数说明
ODP 参数可以分为以下几类:
- 支持动态修改的参数。
- 此类参数修改后立即生效。详细信息,参考 支持动态修改的参数。
- 不支持动态修改的参数。
- 此类参数修改后需要重启 ODP 才能生效。详细信息,参考 不支持动态修改的参数。
- 内存级参数。
此类参数的修改仅生效一次。详细信息,参考 内存级参数。
查看 ODP 配置
当运行 ODP 时,使用 root@proxysys 账号登录 ODP,密码由用户安装 ODP 时自己设置。执行以下命令获取 ODP 配置:
SHOW proxyconfig [LIKE '%var_name%'];
当需要查看所有配置时直接 show proxyconfig 即可,此时结果会以表格的形式展现,包含如下信息:
- name:配置的名称。
- value:配置的值。
- info:配置的介绍。通过该介绍可以了解配置的用途。
- need_reboot:是否重启生效。取值为 true 时,表示修改后需要重启才能够生效。
- visible_level:暂未使用
修改配置
您可以通过以下两种方式修改 ODP 的配置:
- 启动时在 -o 参数后面加上配置的内容,如 -o <var_name>=<var_value>。
- 使用 root@proxysys 登录,通过 alter proxyconfig set <var_name>=<var_value> 修改配置后,对于need_reboot 的配置不会动态生效,必须要重启。
ODP 还有一类配置,只会生效一次,参考下面表格:

有关 ODP 的其它配置,可以通过 show proxyconfig 查看或参考 ODP 文档。
驱动支持
ODP 的 root@proxysys 操作完全兼容 MySQL 协议,使用了 MySQL 协议的 COM_QUERY 字段,ODP 配置管理也可以通过 Java 等 MySQL 驱动进行管理。

浙公网安备 33010602011771号