当前位置: 首页 > news >正文

第六章 OBProxy 路由与使用运维

本章介绍 OBProxy 的概念、路由策略和运维

6.1 OBProxy 简介

OceanBase 数据库代理 ODP(OceanBase Database Proxy,又称 OBProxy)是 OceanBase 专用的代理服务器,OceanBase 用户的数据会以多副本的形式存放在各个 OBServer 上,ODP 则负责接收用户发过来的 SQL 请求,转发用户 SQL 请求到最佳目标 OBServer 上,并将执行结果返回给客户。

OceanBase 在部署完成后,应用可以采用 OceanBase 提供的客户端(obclient)、MySQL 客户端(mysql) 或者其他语言的客户端来访问 OceanBase,OceanBase 以服务的形式提供给应用访问。OceanBase 集群一般包含多个 Zone ,每个 Zone 中又包含一台或多台 OBServer,集群中的每个 OBServer 都可以接收用户的连接并处理请求。

作为数据库的客户端,应用可以直接连接到 OceanBase 集群中的一个 OBServer,但是这样会带来一些问题

  • 如果所连接的 OBServer 发生宕机或其它故障不可用,应用端就不是高可用的了
  • OceanBase 的租户隔离设计,客户端只能连接到有分配租户资源单元 Unit 的 OBServer,这样的话应用就需要感知到租户的 Unit 在集群中的分布了

为解决上述问题,OceanBase 提供了实现一个反向代理服务 OBProxy 来接受来自应用的请求,并转发给OBServer,然后 OBServer 将数据返回给 OBProxy,OBProxy将数据转发给应用客户端。

使用 OBProxy 连接 OceanBase 的部署架构示意图

OBProxy 作为 OceanBase 的高性能且易于运维的反向代理服务器,具有防连接闪断、OBServer宕机或升级不影响客户端正常请求、兼容所有 MySQL 客户端、支持热升级和多集群功能。

作为 OceanBase 数据库的关键组件,ODP 具有以下特性:

  • 高性能转发:ODP 完整兼容 MySQL 协议,并支持 OceanBase 自研协议,采用多线程异步框架和透明流式转发的设计,保证了数据的高性能转发,同时确保了自身对机器资源的最小消耗。
  • 最佳路由:ODP 会充分考虑用户请求涉及的副本位置、用户配置的读写分离路由策略、OceanBase 多地部署的最优链路,以及 OceanBase 各机器的状态及负载情况,将用户的请求路由到最佳的 OBServer,最大程度的保证了 OceanBase 整体的高性能运转。
  • 连接管理:针对一个客户端的物理连接,ODP 维持自身到后端多个 OBServer 的连接,采用基于版本号的增量同步方案维持了每个 OBServer 连接的会话状态,保证了客户端高效访问各个 OBServer。
  • 专有协议:ODP 与 OBServer 默认采用了 OceanBase 专有协议,如增加报文的 CRC 校验保证与 OBServer 链路的正确性,增强传输协议以支持 Oracle 兼容性的数据类型和交互模型。
  • 易运维:ODP 本身无状态支持无限水平扩展,支持同时访问多个 OceanBase 集群。可以通过丰富的内部命令实现对自身状态的实时监控,提供极大的运维便利性。

OBProxy 重要概念

  • SQLParser:轻量的sql解析,判断出客户端的sql请求所涉及的表的主副本在哪台机器上,将请求路由至主副本所在的机器上
  • LDC路由:主要对于读写分离的场景,根据OBServer 和OBProxy配置的region(区域)和LDC(逻辑机房),将请求发送给本地的副本(后面proxy路由策略会详细说明)
  • 读写分离部署:对于读写分离的场景,OBProxy会把请求优先发送到本地的只读副本
  • 黑名单:OBProxy在路由过程中,如果发现OBServer不可用,则把该Server 加入到黑名单

OBProxy 最低配置配置要求比OBServer要低很多,使用资源也较小,资源不足的时候,可以跟OBServer部署在一起。系统要求如下:

  • OS: Linux Redhat 7u x86-64 及以上
  • OS内核:3.10 及以上版本
  • CPU: 2 核及以上
  • 内存:1G 及以上
  • 磁盘空间:对磁盘大小没有特别需求,推荐10G 及以上,主要用于存放OBProxy的应用日志

6.2 OBProxy 核心功能

6.2.1 OBProxy 请求路由

总体流程如上图所示,大致分为以下几个步骤:

  1. sql parser:根据用户sql解析出抽象语法树
  2. 获取table schema:根据抽象语法树提取出的表名从table cache里面找到对应的schema
  3. 提取分区表达式:根据table schema和抽象语法树提取分区表达式
  4. 计算 Partition id :计算分区表达式和分区类型( hash 、 range 或者其他分区类型),计算 Partition id
  5. 计算 location :根据 Partition id 从Partition cache 里面找到对应的 location并且根据请求类型(读主或是读备)对location 进行优先级排序

6.2.2 OBProxy 连接管理

OBProxy 连接管理主要特性

  • 在 OBServer 宕机/升级/重启时,客户端与OBProxy的连接不会断开,OBProxy 可以迅速切换到正常的 Server 上,对应用透明
  • OBProxy 支持用户通过同一个 OBProxy 访问多个 OceanBase 集群
  • Server session 对于每个 client session 独占
  • 同一个 client session 对应 Server session 状态保持相同(session 变量同步)

OBProxy与OB集群(OBServer)保持长连接,客户端一般通过连接池的方式连接到OBProxy

6.3 OBProxy 部署

6.3.1 集中部署

Obproxy集中部署的方式主要用于外部上云,方便外部用户使用Mysql协议访问OceanBase。外部用户使用的Mysql客户端种类繁多,Obproxy需要解决可能遇到的各种Mysql兼容问题。Obproxy与Nginx这类Web服务程序类似,多个Obproxy进程之间不需要通讯,单个Obproxy无状态,即使宕机重启也不会导致数据一致性问题,所以Obproxy实现平滑热升级和宕机快速重启便可以基本满足 HA 的需求。

OceanBase 外部上云的整个链路如下图所示,用户的连接请求通过 SLB 的四层负载均衡,均匀分布到各个 Obproxy 上; Obproxy 通过解析用户的 SQL 请求,做七层负载均衡,尽量将用户 SQL 请求转发到数据所在的OceanBase 服务器上。同时 OCP 负责集中部署的 Obproxy 集群的监控,升级和配置管理等运维功能。

6.3.2 客户端部署

将 Obproxy 作为客户端部署在应用的虚拟机上,这种方式用于阿里/蚂蚁内部上云,用户访问本地的 Obproxy 相比访问集中部署的 Obproxy 可以减少一次网络销,能节省 0.2ms~1ms 的响应延时。

Obproxy 转发 request/response 时内部总耗时大约 30us 左右,但 Obproxy 能将用户请求路由到数据所在的OceanBase 服务器上,从而有利于降低请求的响应时间。

Obproxy 部署在客户端时仍然通过 OCP 来管理,但由于应用的虚拟机特别多,应用的 PE 来运维 Obproxy 有一定的难度,所以 Obproxy 通过 OCP 做了一些批量运维工具, Obproxy 周期性向 OCP 汇报状态和统计信息, OCP 实时监控各个Obproxy 的运行状态,通过 OCP 可以对Obproxy 进行批量热升级和配置更新。

同时在应用的虚拟机上部署了一个守护进程,如果发现 Obproxy 宕机,会立即重启 Obproxy ,尽量减少由于 Obproxy宕机导致的服务不可用时间。

6.2.3 OBProxy 和 Config Server

ConfigServer是OCP平台提供的OB集群物理入口管理服务,用来获取集群的连接信息,OB和客户端都是通过config_url 来跟 ConfigServer 交互。OB集群会将集群位置信息写入到ConfigServer中,同时,客户端指定OB集群的config_url访问ConfigServer也可以获取该集群的物理信息,从而进行集群的访问。

Config Server url是一个web service api,作为ocp一部分会自动启动,ocp部署的时候一般也会做高可用,api 不会 down 掉。

由于OceanBase 支持多租户 , 每个租户对应一个 MySQL 实例。因此访问OceanBase 的用户名需要指定租户名。又由于 proxy 支持 OceanBase 的多集群部署,因此通过 proxy 访问 OceanBase 服务时, 还需要指定集群名 , 格式有四种:

  • username@tenantname#clustername , 如 root@trade#xxbank
  • clustername:tenantname:username , 如 xxbank:trade:root
  • clustername-enantname-username, 如 xxbank-trade-root
  • clustername.tenantname.username, 如 xxbank.trade.root

6.2.4 OBProxy 两种启动模式

测试模式

  • 主要用于现阶段开发调试,无需依赖 ConfigServer
  • ConfigServer是OCP平台提供的OB集群物理入口管理服务,是一个web api的服务
  • 测试模式通过指定集群的RSList(ip列表)来启动

生产模式

  • ObProxy 可以通过指定 Config Server 提供的 config_url 来启动,Config Server 服务可以协助获取该集群的配置信息。同一个 Config Server 可以保存多个 OB集群的 RSList 信息,使 OBProxy 能为多个 OB 集群同时提供服务。
  • 在连接 ObProxy 时,其用户名类似 root@sys#cluster,其中 root 为用户名,sys 为租户名,cluster 为集群名

6.4 OBProxy 路由策略

6.4.1 路由相关概念

这些基础概念和OBproxy路由密切相关,根据不同的配置,Obproxy 进行综合的路由排序:

  1. LDC 配置:
  2. 本地: 同城同机房(IDC相同)
  3. 同城: 同城不同机房(IDC不同,Region 相同)
  4. 异地: 不同的地域(Region不同)
  5. ObServer 状态: 常态vs 正在合并
  6. 租户的Zone 类型: 读写型vs 只读型
  7. 路由精准度: 优先精准度高的
  • OBproxy 中有目标Partition 的路由信息(PS)
  • OBproxy 中没有Partition的路由信息,只有租户的路由信息(TS)
6.4.1.1 IDC,LDC 和 Region
  • IDC : 互联网数据中心,可以简单看成一个物理机房,IDC 记录 OB 集群的机房信息
  • LDC :logical data center ,是对IDC 的一种逻辑划分,OB 在多地多中心部署时, 会以 Region 和 Zone 的单位对所有 OBServer 进行划分 ,这也是一种 LDC 部署 , 这种情况下 OB 的客户端路由和 OB 内部的 RPC 路由被称为 LDC 路由
  • Region:地域信息,通常代表一个城市,包含一个或多个 IDC,每个 IDC 部署一个或多个 Zone。

一个 OceanBase 集群可以包含若干个 Region,每个 Region 包含若干个 IDC,每个 IDC 部署若干个 Zone。

6.4.1.2 OceanBase 集群的 IDC 配置
-- 设置 zone 的 region/idc
alter system modify zone 'z1' set region = 'SHANGHAI';
alter system modify zone 'z1' set idc ='zue';-- 检查 zone 的 LDC 设置内容是否生效
select * from __all_zone;
6.4.1.3 OBProxy 的 IDC 配置

OBProxy 的 LDC 的支持全局级别和 session 级别

全局级别

  • 配置项 proxy_idc_name 用来控制全局级别的当前IDC机房信息, 默认为空。
  • 配置项的设置可以通过 启动参数/登陆修改/ocp配置项 更新进行
  • 在proxy 的启动脚本中使用 -i 机房名启动传入
  • 或者 proxy 运行后通过 alter proxyconfig set proxy_idc_name='idc_name';设置

session级别

  • 设置用户变量set @proxy_idc_name=‘xx’控制session级别的当前机房信息, 默认不指定

检查 LDC 匹配情况 OBProxy 命令

show proxyinfo idc;

6.4.2 OBProxy 主要路由策略

写请求:

  • 写请求路由到 ReadWrite Zone 的主副本

读请求:

  • 强一致性读
  • 弱一致性读
  • 主备均衡路由策略(默认)
  • 备优先读策略
  • 读写分离策略

读写默认都走主副本

6.4.2.1 强一致性读路由策略
  • 默认情况,需要读取Partition主的数据
  • 即SQL必须转发到涉及Partition的leader Server 上执行, 以此保证获取到实时最新的数据
  • 强一致性读适用于对读写一致性要求高的场景
6.4.2.2. 弱一致性读路由策略:配置弱一致性读

系统变量

  • 用户设置当前租户全局系统 session 变量 set global ob_read_consistency='weak' ,对当前租户所有会话都生效(当前会话不生效)
  • session 级别, set ob_read_consistency='weak',只对当前正在连接的 session(会话)生效

sql hint

  • 用户在 select 中加 /*read_consistency(weak)*/ 的 Hint,仅本语句生效
6.4.2.3 弱一致性读:主备均衡路由策略(默认)
  • 可以读主也可以读备,流量按照副本均匀分配
  • 弱一致性读时, 或者建立连接时, 或者强一致性读找不到Partition时, 或者强一致性读Partition主不可用时, 按照:本地常态-> 同城常态-> 本地合并-> 同城合并-> 异地常态

顺序如下

  1. 选取本机房不在合并的副本;
  2. 选取同地域机房不在合并的副本;
  3. 选取本机房在合并的副本;
  4. 选取同地域机房在合并的副本;
  5. 随机选取非本地域机房不在合并的副本;
  6. 随机选取非本地域机房在合并的副本;
6.4.2.4 弱一致性读:备优先读策略

OBProxy 支持备优先读路由策略,通过用户级别系统变量 proxy_route_policy 控制备优先读路由。备优先读仅在弱一致性读时生效,且优先读 follower 而非主备均衡选择。

使用 root 用户登录集群的 sys 租户后,运行下述语句对系统变量 proxy_route_policy 进行设置: SET @proxy_route_policy='[policy]';

6.4.2.5 弱一致性读:读写分离策略

读写分离部署,要客户端将写请求路由到 ReadWrite Zone 主副本,将弱一致性读请求路由到 ReadOnly Zone

使用root 用户登录到集群的业务租户,运行下述语句设置Global 级别系统变量ob_route_policy 为对应取值即可,默认取值 为 readonly_zone_first : set @@global.ob_route_policy=readonly_zone_first;

6.5 OBProxy 执行流程

6.5.1 OBProxy的执行流程:

  1. parse sql,通过简易 parse,解析出 sql 涉及的 table name、database name
  2. fetch route entry,根据用户的 tenant name、database name、table name、Partition id 向OBServer 拉取该 Partition 的路由表
  3. sort route entry,根据各种相关属性对路由表中ip进行排序
    • read_consisitency:强一致性读or弱一致性读
    • 目标Server 状态:正在合并or常态
    • 路由精准度: PS or TS
    • LDC 匹配: 本地、同城、异地
    • Zone 类型
    • 读写分离的 ob_route_policy 取值
  4. filter by congestion,从路由表中依次尝试目标 ip,通过黑名单进行过滤
  5. forward request,将用户请求转发给目标 Server

6.5.2 OBProxy 使用限制

proxy parser 在根据 SQL 选择 Server 时,有以下几点特殊的逻辑:

  • proxy parser 只解析 Begin/START TRANSACTON/SET/ 和其他 DML,如果遇到其他单词开头的语句,proxy 的parser 会直接跳过,认为该语句不包含表名
  • proxy parser 会按照第一条包含实体表名的 stmtement 进行路由,如果整个 stmtement 都不包含表名,则将请求发送至上一条 SQL 所发送的 Server

OBServer 会根据执行计划的类型,来告诉proxy是否将请求路由至正确的 Server ,如果路由失败,proxy 会更新location,当前的反馈机制如下:

  • Server 返回第一条DML的命中情况

6.5.3 OBProxy 推荐SQL语句

以下几种情况(select可以等价替换成 update/delete/replace/insert,下同),proxy 能够将请求发送至正确的Server ,并且 Server 能够按照 proxy 的命中情况进行反馈:

  • begin; select * from t1; commit;
  • set @@autocommit = 1; insert into t1 values(); set @@autocommit = 0;
  • select * from t1; insert into t2 values;
  • set @@ob_trx_timeout = 10000000; begin; select * from t1; commit;

以下几种情况,proxy会将请求发送至上一个SQL所使用的Server

  • create table t1 (id int Primary key); create table t2 (id int Primary key);

6.5.4 OBProxy 不推荐SQL语句

以下几种情况(第一个DML是非实体表),proxy 能够将请求发送至正确的 Server ,但是 Server 反馈的信息可能不准,不建议使用:

  • select '1'; select * from t1;
  • select '1' from dual; select * from t1;

以下几种情况,proxy 可能能够将请求路由至正确的 Server ,但是 Server 端反馈信息可能不准,不建议使用:

  • create table t1 (id int Primary key); insert into t1 values();

以下几种情况,proxy 会强制将请求路由至上一次使用的 Server ,Server 端反馈信息可能不准,不建议使用:

  • show warnings; select * from t1;
  • show count(\*) errors; select * from t1;

6.6. 使用和运维

6.6.1 使用守护进程启动

ObProxy 无状态,即使宕机重启也不会导致数据一致性,所以 ObProxy 在部署时都带有一个守护进程,周期性检查Obproxy 的健康程度,一旦发现宕机就立即重启 ObProxy。

守护进程使用方法

obproxyd.sh -c COMMAND -e ENVIRONMENT [-n APP_NAME] [-i IDC_NAME]

修改 obproxyd.sh 的变量:

OBPROXY_CONFIG_SERVER_URL='http://api.test.ocp.oceanbase.alibaba.net/services?Action=GetObProxyConfig&User_ID=alibaba-inc&UID=ocpmaster' 

启动方法:./bin/obproxyd.sh -c start -e alipay -n workshop

Kill 掉 OBProxy 进程后,守护进程自动拉起 obproxy 进程,恢复服务

6.6.2 OBProxy 配置项

如何修改配置

  • 系统租户,通过 OBProxy 连接 OceanBase 集群
  • 涉及配置项的内部命令有两种,如下:
  • show proxyconfig,展示 proxy 内部各配置项属性以及 Config Server 的配置信息
  • alter proxyconfig set key=value,更新指定 config 配置项值
  • 更新命令只对除 Config Server 配置信息之外的其他配置项有效,Config Server 配置信息只能通过 Config Server 来更新
  • 有些配置需要重启 Proxy 才生效(参考 need_reboot 这列的值)

OBProxy 配置项可以分为3种类型况:

  1. proxy 写到本地etc文件夹中配置文件的配置项,这些配置项可供用户根据使用场景进行配置和更新
  2. proxy 内部自己使用,对一般用户不可见的配置项,不会注册到内部表中
  3. proxy 从Config Server 中获取到的配置信息(包括版本号、meta table信息、cluster 信息、bin url 和更新时间),这些信息只用来展示Config Server 的配置,不会注册到内部表或者dump到本地配置文件中,并且它们全部以字符串“json_config”开头,查询时可以使用like进行过滤

上述的前两种配置信息会在本地生产一个配置文件,默认保存在 proxy 运行目录的 etc 目录下,文件名为

obproxy_config.bin。第三种配置信息仅保存在 proxy 内存中。如果用户使用 alter proxyconfig 内部命令,则会更新本地配置文件 `obproxy_config.bin`。

show proxyconfig 支持 like 模糊匹配(支持'%'和'_'的匹配)。

6.7 常见问题

6.7.1 启动失败

排查手段

  • 机器是否存在 hostname :输入hostname -i, 确认 host ip 是否存在
  • 目录是否存在,权限是否正确:确保当前目录下有读、写、执行的权限
  • 端口是否被占用:使用 obproxyd.sh 启动 OBProxy, 使用的端口为 2883
  • 启动环境是否指定正确:如果通过 obproxyd.sh启动, 需要使用 -e 参数指定 OBProxy 运行环境

ObProxy 只要能启动成功、建立连接,就解决 90% 的问题

6.7.2 无法建联

OBProxy 启动成功后, 当使用客户端进行连接 OBProxy 的时候出错

6.7.2.1 Can't connect

错误消息类似 ERROR 2003 (HY000): Can't connect to MySQL Server on '127.1' (111)

可检查所需建立连接的 obproxy, obproxy 是否存在。

6.7.2.2 权限错误/租户名错误/认证错误

错误样例

  • 权限错误: ERROR 1045 (42000): Access denied for user ‘XXXXXXXXX’
  • 租户名错误: ERROR 5160 (HY000): invalid tenant name specified in connection string
  • 认证错误: ERROR 2013 (HY000): Lost connection to MySQL Server at ‘reading authorization packet’, system error: 0

排查手段

用户名/租户名/集群名/密码是否正确

  • 如果不确认是否正确, 可以直接连接 OBServer 确认该信息是否正确, 注意连接 OBServer 的时候不能带集群名

本机 mysql 版本是否过低

  • MySQL 5.7.8 之前版本, 用户名长度超过 16 字节会被截断
  • 5.7.8 版本之后版本用户长度超过 32 字节会被截断
  • 这里的用户名包含完整的 username@tenantname#clustername

本地 json 配置集群是否和远程 json 文件一致

  • 该配置文件主要用于确认你需要连接的OB集群是否存在
  • 可以通过检查本地 json 文件,和 curl configure url 来进行比较
  • 该配置文件主要用于确认你需要连接的 OB 集群 ObRegion 是否存在搜索关键字: ObRegion

6.7.3 慢查询

排查手段

  • proxy 慢查询? - 分析 proxy 日志
  • Server show trace? - 分析 Server 日志
  • 远程执行? 为什么?
  • 机器负载如何?

慢查询配置项

  • slow_transaction_time_threshold=5s
  • 默认为5s

修改慢查询配置项

-- 将慢查询阈值设置为100ms
alter proxyconfig set slow_transaction_time_threshold='100ms';

关键日志:

  • update_cmd_stats -> Slow query
  • Slow transaction -> Slow transaction

慢查询日志说明

  • 两种日志时间记录命名方式一致,如果打印 Slow query 日志, 必打印 Slow transaction, 无论是否存在一个业务层面显示开启的事务
  • 使用 conn_id 可以精确的定位一个连接的上的SQL执行情况

关于远程调用

  • trans_type=0 单 Partition DML
  • trans_type=1 AC=1 的 select 语句
  • trans_type=2 Partition 分布式事务(单机,或者跨机)

OBProxy 慢查询日志和 OBServer 日志区别

  • OBProxy 会统计应用端的请求、请求发送给 OBProxy,OBProxy 发送给 OB集群等请求的耗时
  • OB 集群日志统计的慢 sql,主要从数据内核的事务层面,来分析执行效率

6.8 OBProxy 参考资料

  • OB 官网 ODP 3.1.0 用户文档
http://www.xdnf.cn/news/1134847.html

相关文章:

  • rLLM:用于LLM Agent RL后训练的创新框架
  • Git版本控制完全指南:从入门到精通
  • Nginx,MD5和Knife4j
  • NLP:LSTM和GRU分享
  • 人工智能之数学基础:神经网络之多样本矩阵参数求导
  • C++ - 仿 RabbitMQ 实现消息队列--sqlite与gtest快速上手
  • 光纤基础知识
  • lua(xlua)基础知识点记录一
  • IIS-网站报500.19错误代码0x8007000d问题解决
  • “重复”定义函数的睿智(Python/与ai助手“智普清言”深度交流)
  • Java后端开发核心笔记:分层架构、注解与面向对象精髓
  • java解析word文档
  • Linux 716 数据库迁移
  • x86版Ubuntu的容器中运行ARM版Ubuntu
  • 零基础学Vue3组件化开发
  • 统计功效是什么?
  • VR 污水厂初体验:颠覆传统认知​
  • 广州 VR 森林防火系统功能探究​
  • AI应用核心转向Context Engineering
  • 在UE中如何操作视图的大小,方位,移动
  • FPGA基础 -- Verilog 访问寄存器数组的指定位示例
  • 详解SPFA算法-单源最短路径求解
  • AI Agent开发学习系列 - langchain之LCEL(2):LCEL 链式表达解析
  • 高性能上位机界面设计范式:C#与C++/C开发调试无缝衔接
  • 《图解技术体系》Four Implementation Methods of Distributed Transactions
  • 《设计模式之禅》笔记摘录 - 7.中介者模式
  • FATFS文件系统原理及其移植详解
  • 042_封装的实现(属性私有化 / 方法公开)
  • Gradle vs Maven:构建工具世纪对决 —— 像乐高积木与标准模型之间的选择艺术
  • LeetCode经典题解:141、判断链表是否有环