关于数据库:高性能数据访问中间件-OBProxy四一文讲透连接管理

2次阅读

共计 10015 个字符,预计需要花费 26 分钟才能阅读完成。

上篇内容咱们讲到 OBProxy 的问题排查,将大家在应用 OBProxy 时可能遇到的问题一一剖析,并给出通过实际验证的解决方案。从本篇开始,我将介绍 OBProxy 在 OceanBase 分布式架构中的作用和原理,帮忙你更透彻地理解 OBProxy,实现“好用”和“用好”。 同时,OBProxy 在上百家企业的继续运行,咱们积攒了大量的工程实践经验,也将遇到的问题作为案例,随同 OBProxy 的原理解说分享给你,供大家参考。

本篇从 OBProxy 的重要个性连贯治理讲起,其中,对连贯映射关系、session 状态同步等内容,你能够将其与单机数据库比拟,领会分布式系统和单机零碎的异同。

 

OBProxy 的连贯治理步骤及原理

在正式解说之前,我先介绍一下本篇内容的技术背景。咱们晓得,OBProxy 为用户提供了数据库接入和路由性能,用户连贯 OBProxy 就能够失常应用 OceanBase 数据库。用户在应用数据库性能时,OBProxy 和 OBServer 进行交互,且交互流程对用户通明,连贯治理就是该交互过程中的关键点之一。
OBProxy 的连贯治理有三个个性:

  1. 代理个性:OBProxy 既是客户端,也是服务端,还须要保障交互行为合乎 MySQL 协定标准。
  2. 性能个性:OBProxy 实现了很多的连接功能个性,如拜访不同集群、不同租户,再如反对主备库、分布式下的 ps 性能,以及兼容 kill、show processlist 等命令。
  3. 高可用个性:OBProxy 能够解决超时、机器状态变动、网络状态变动等问题,屏蔽后端异样,让用户无感知。

接下来咱们按应用 OBProxy 的操作步骤一步一步来解说连贯治理内容。

 

第一步:用户登录

1. 登录信息

在登录 OBProxy 时,咱们须要填写数据库 IP 和 PORT、用户名、明码等信息,对于用户名,OBServer 的格局为 user_name@tenant_name。因为 OBProxy 能够代理不同的集群,所以格局又变为 user_name@tenant_name#cluster_name,字段含意如下:

  • user_name:登录的用户名,明码保留在 OBServer 中,OBProxy 只做登录报文转发不做明码校验。
  • tenant_name:OBServer 是多租户架构,tenant_name 示意拜访的租户名。
  • cluster_name:集群名,OBProxy 反对拜访多个集群,不同集群通过 cluster_name 辨别。

有了这些信息,咱们就能够通过 JDBC 驱动、MySQL 命令行、Navicat 等工具连贯 OBProxy 拜访数据库了。

你可能会纳闷,用户登录时 OBProxy 如何找到对应的机器呢?这就要依赖 OCP 零碎(通过 obproxy_config_server_url 配置项指定 OCP 的 url 地址拜访),OCP 会保留集群名和集群的机器列表,OBProxy 通过拜访 OCP 获取这些信息,整个流程如下图。

有了租户的机器列表,咱们就能够进行路由转发了。这里须要留神的是,本系列第二篇介绍的 rslist 启动形式会省略上图中的步骤 2 和步骤 3,这种形式只反对拜访一个集群。

 

2. 登录认证

找到机器后,咱们就能够登录认证了,要害信息是用户名和明码,MySQL 协定中(官网图片中展现了 Handshake 相干报文,理论流程中第 2 步完结后服务端须要回复一个 OK 或者 Error 报文给客户端)的交互流程如下图。

OBProxy 作为代理组件,要兼容 MySQL 行为,会有更多步骤。咱们以 Java 程序连贯数据库为例阐明整个流程,在 Java 程序中,登录代码只有一行:

conn = (Connection) DriverManager.getConnection(URL, USER_NAME, PASSWORD);

但背地的原理比较复杂,如下图所示。

在实现协定的登录步骤(第 8 步)后,MySQL 协定层的登录交互就完结了,但 JDBC 会发送一些初始化 SQL,也属于登录过程一部分,图中第 9 步示意该过程。初始化 SQL 有多条,内容如下:

set autocommit=1, sql_mode = concat(@@sql_mode,',STRICT_TRANS_TABLES')
set names utf8
SELECT @@max_allowed_packet,@@system_time_zone,@@time_zone,@@auto_increment_increment,@@tx_isolation AS tx_isolation,@@session.tx_read_only AS tx_read_only
select @@version_comment, @@version limit 1

下面 SQL 执行实现后,Java 程序就能够发送业务 SQL 了。

 

3. 常见登录问题

当初的你曾经明确了 OBProxy 的登录原理,就能够在登录失败时疾速地定位和解决问题了。常见的问题及起因如下:

  • 用户名或明码谬误,能够通过直连 OBServer 确定。
  • OCP 故障,无奈拉取集群机器列表,能够通过 curl 命令拜访 OCP 的 url 确定。
  • OBServer 执行获取机器列表或初始化 SQL 失败,须要日志排查定位。
  • 超过最大连接数设置或不在白名单中,后文将为你具体解读。

对于上述问题,你能够通过本系列第三篇文章提到的查看 obproxy_error.log 形式解决。

 

第二步:连贯治理

登录胜利后,客户端 <->OBProxy<->OBServer 之间的网络连接便建设起来,此时 OBProxy 只是和其中一台 OBServer 建设了连贯。随着 SQL 申请的到来,如果路由到新的 OBServer,会和新的 OBServer 建设连贯。在此过程中波及连贯的映射关系、状态同步和连接功能个性,接下来一一解读。

1. 连贯的映射关系

连贯映射次要讲客户端连贯和服务端连贯之间的关系,咱们先从一个客户端连贯说起。当客户端和 OBProxy 建设一个连贯后,OBProxy 会和前面 N 个 OBServer 建设连贯,整个关系如下图所示。

能够看到,OBProxy 按需和两台 OBServer 建设了连贯。这两个连贯只属于这一个客户端连贯,不会被其余客户端连贯复用。连贯映射的关键点就是须要用 id 标识出每一个连贯并记录 id 之间的映射关系,咱们能够将上图形象成模型:
App<—–[proxy_sessid1]—->OBProxy<—[server_sessid1]—–>OBServer1

                                                                            <---[server_sessid3]----->OBServer3

这样咱们就能够用 proxy_sessid 惟一标识 App 和 OBProxy 之间的连贯,用 server_sessid 惟一标识 OBProxy 和 OBServer 之间的连贯。当 SQL 执行谬误、执行慢等状况呈现,会将映射关系打印到日志中,这样就将 App 和 OBServer 关联起来,进行全链路问题定位。

 

2. 状态同步

一个客户端连贯对应多个服务端连贯,要保障执行后果的正确性,就要求多个服务端连贯的 session 状态是统一的。那么,状态不同步会导致什么问题?咱们举个反例,假如用户执行上面的 SQL 命令:

set autocommit=1;
insert into t1 values(1);
insert into t2 values(2);

执行过程如下:

  • set autocommit= 0 发给 OBServer1
  • insert into t1 values(1)发给 OBServer1
  • 进行连贯切换,insert into t2 values(2)发给 OBServer2

对于第三条 SQL,OBProxy 和 OBServer2 的连贯并未同步连贯状态 autocommit=1,这样就可能导致第三条语句 insert into t2 并未提交事务。
正确的步骤是 OBProxy 在给 OBServer2 发送 INSERT SQL 前,先同步 autocommit 变量的值。OBProxy 通过版本号机制解决了状态同步的问题,实现了 database、session variables、last_insert_id、ps prepare 语句的状态同步,保障性能的正确性。

 

3. 连接功能个性

和单机数据库不同,OBProxy 扭转了连贯的映射关系为 M:N,因而有些连接功能须要做额定解决。举个例子,用户通过 show processlist 查看连接数,此时他心愿看到的是客户端和 OBProxy 之间的连接数,而不是 OBProxy 和 OBServer 之间的连接数。上面咱们对常见的连接功能开展具体介绍。
连贯粘性。OBProxy 还未实现所有性能的状态同步,如事务状态、长期表状态、cursor 状态等。对于这些性能,OBProxy 只会将后续申请都发往状态开始的节点,这样就不须要进行状态同步,而毛病是无奈充分发挥分布式系统的劣势。因而,咱们依据性能重要水平,逐渐反对相干性能的分布式化。
show processlist 和 kill 命令配套应用。show processlist 用于展现客户端和服务端之间的连贯,对于 OBProxy,show processlist 只展现客户端和 OBProxy 之间的连贯,不展现 OBProxy 和 OBServer 之间的连贯。kill 命令用于杀死一个客户端连贯,客户端连贯敞开后,OBProxy 也会敞开对应的服务端连贯。对于 OBProxy 的 kill 命令,须要先获取对应的 id,如下图的 Id 列内容(show proxysession 和 show processlist 性能相似,show proxysession 是 OBProxy 专属命令)。

负载平衡影响。因为 OBProxy 对 show processlist 和 kill 命令做了解决,所以 show processlist 和 kill 命令只有都发往同一台 OBProxy 能力失常工作。在私有云等环境,OBProxy 后面有负载平衡,负载平衡前面挂在多个 OBProxy 上,此时,如果执行 show prcesslist 和 kill 命令是两个不同的连贯,负载平衡组件可能将申请发往不同的 OBProxy,在这种状况下,咱们最好不要应用相干命令。
介绍完 OBProxy 的连贯映射关系、状态同步和连接功能个性的技术原理后,你可能会感觉如果用 HAProxy 等一般代理,很多中央就会简略很多,比方:

  • 用户连贯一般代理后,一般代理只会和一台 OBServer 建设连贯,连贯映射是 1:1 的治理。
  • 一般代理的连贯映射为 1:1 的关系,就不须要做状态同步,连接功能也不须要非凡解决,间接转发即可。

那么,为什么还举荐应用 OBProxy,它的劣势在哪里呢?

  • 一般代理无奈做高可用容灾,OBProxy 通过外部表、错误码等信息,会探测出 OBServer 状态(降级、宕机等),对 OBServer 节点进行拉黑和洗白。
  • 一般代理无奈充分发挥 OceanBase 数据库的性能,连贯建设后,一般代理无奈将 SQL 发往其余 OBServer 节点,有时执行链路会更长。OBProxy 能够精准路由,并实现读写拆散等个性。
  • 一般代理性能无限,无奈适配 OceanBase 主备库、复制表、LDC 架构等个性。

以上就是应用 OBProxy 的步骤及其原理,如果想理解更深层次的连贯原理常识,咱们须要追溯到 TCP 协定。因为 OBProxy 的连贯基于 TCP 协定,理解 TCP 协定的连贯机制就能更透彻的把握连贯治理的性能及连贯问题定位等常识。

 

延长知识点 1:基于 TCP 协定的连贯机制

TCP 参数

OBProxy 在代码层面设置了 TCP 的 no_delay 和 keepalive 属性,以保障低提早、高可用等个性。

  • no_delay 属性 通过禁用 TCP Nagle 算法解决提早问题。在 Linux 的网络栈中默认启用 Nagle 算法,用于解决网络报文小分组呈现,但会导致网络报文发送提早。咱们曾在生产环境中遇到 TCP 未禁用 Nagle 算法的状况,导致一条 SQL 发送耗时 40ms 左右,这是不满足业务要求的。
  • keepalive 属性 用于故障探测。及时发现机器故障,将有效的连贯敞开,是 TCP 层高可用一部分。

这两个属性能够通过 OBProxy 的配置项进行配置,举荐配置如下:

## 和 OBServer 的 tcp 连贯设置
sock_option_flag_out = 3;  --  这是个二进制位参数,bit 0 示意否是启用 no_delay,bit 1 示意是否启用 keepalive。3 的二进制是 11,示意启用 no_delay 和 keepalive
server_tcp_keepidle = 5;  --  启动 keepalive 探活前的 idle 工夫,5 秒。server_tcp_keepintvl = 5;  -- 两个 keepalive 探活包之间的工夫距离,5 秒
server_tcp_keepcnt = 2;  --  最多发送多少个 keepalive 包,2 个。最长 5 +5*2=15 秒发现 dead_socket。## 和客户端的 tcp 连贯设置
client_sock_option_flag_out = 3;  --  同上
client_tcp_keepidle = 5;  --  同上
client_tcp_keepintvl = 5;  -- 同上
client_tcp_keepcnt = 2;  --  同上

咱们在生产环境中曾经验证了 no_delay 和 keepalive 属性,你能够放心使用。对于其余 TCP 参数,咱们间接应用默认值,还没有调参的需要。

 

超时参数

咱们在排查问题时,偶然会遇到 TCP 连贯断了的状况,但无奈确定是客户端断连还是服务端断连。依据教训判断,往往是超时机制触发的。这里针对几种超时机制介绍一下全链路解决方案。

1.JDBC 超时

(1)socketTimeout

socketTimeout 是 Java socket 的超时工夫,指的是业务程序和后端数据库之间 TCP 通信的超时工夫,如果业务程序发送一个 MySQL Packet 后,超过 socketTimeout 的工夫还没有从后端数据库收到 Response 报文,这时候 JDBC 会抛出异样,程序执行失败。
socketTimeout 单位是毫秒,能够通过以下形式设置:

jdbc:mysql://$ip:$port/$database?socketTimeout=60000
(2)connectTimeout

connectTimeout 是业务程序应用 jdbc 跟后端数据库建设 TCP 连贯的超时工夫,也相当于是在 connect 阶段的 socketTimeout,如果 TCP 连贯超过这个工夫没有建胜利,jdbc 会抛出异样。
connectTimeout 的单位是毫秒,能够通过以下形式设置:

jdbc:mysql://$ip:$port/$database?socketTimeout=60000&connecTimeout=5000
(3)queryTimeout

queryTimeout 是业务程序执行 SQL 时,jdbc 设置的本地的超时工夫。在业务调用 jdbc 的接口执行 SQL 时,jdbc 外部会开启这个超时机制,超过 quertTimeout 没有执行完结,jdbc 会在以后连贯上发送一个 kill query 给后端数据库,并给下层业务抛一个 MySQLTimeoutException。
queryTimeout 单位是秒,能够通过以下形式进行设置:

1、通过 jdbc 的 Statement.setQueryTimeout 接口来设置
    int queryTimeout = 10;
java.sql.Statement stmt = connection.CreateStatement();
stmt.setQueryTimeout(queryTimeout);

这里须要特地阐明的是,如果要应用 queryTimeout,倡议设置一个比 socketTimeout 小的值,否则会先触发网络超时从而导致断连贯。但咱们并不举荐应用 queryTimeout,你能够应用 OceanBase 数据库的 ob_query_timeout 属性。

(4)JDBC 超时实际

JDBC 重要的几个超时参数,均能够设置到连接池的 ConnectionProperties 中,或者 JDBC URL 上:

参数 阐明 推荐值
socketTimeout 网络读超时工夫,如果不设置默认是 0,应用 OS 默认超时工夫 5000ms
connectTimeout 链接建设超时工夫,如果不设置默认是 0,应用 OS 默认超时工夫 500ms

这些参数是咱们依据蚂蚁外部 OLTP 业务总结而来,供你参考。

 

2.OceanBase 数据库超时

OceanBase 数据库有本人特定的超时参数,这些参数和 JDBC 的不同在于OceanBase 触发超时后的行为是返回 ERROR 报文而不是断连贯

(1)ob_query_timeout

ob_query_timeout 是 OBServer SQL 级别的超时参数,能够通过 hint 或者 session 变量设置,示意执行一个 SQL 超时工夫。当业务的 SQL 在数据库执行工夫超过 ob_query_timeout 设置的值后,OBServer 会返回一个 ERROR 包给客户端,错误码是 4012。ob_query_timeout 的默认值是 10000000(10s), 单位是微秒。能够通过以下几种形式设置:

// Java 程序示例

// 1、通过 sql 设置 session 变量或者 global 变量,设置实现之后,以后连贯失效
stmt.execute("set @@ob_query_timeout = 10000000");
stmt.execute("set @@global.ob_query_timeout = 10000000");

// 2、通过在业务 sql 中增加 hint 设置,以后 sql 失效
String sql = "/*+ QUERY_TIMEOUT(10000000)*/ select count(*) from XXX";
stmt.execute(sql);
(2)ob_trx_timeout

ob_trx_timeout 是 OBServer 的事务超时工夫,是一个 session 变量,当一个事务执行超过 ob_trx_timeout 还没有完结时,OBServer 同样会返回 4012 的 ERROR 报文给客户端。如果超时产生时,事务还没有提交,那么 OBServer 会回滚这个事务;如果产生在 commit 阶段,因为事务状态未定,OBServer 不会被动回滚它。ob_trx_timeout 默认值是 100000000(100s),单位是微秒,能够通过以下形式设置:

// Java 程序示例

// 通过 sql 设置 session 变量或者 global 变量,设置实现之后,以后连贯失效
stmt.execute("set @@ob_trx_timeout = 100000000");
stmt.execute("set @@global.ob_trx_timeout = 100000000");
(3)兼容 MySQL 超时

除了上述 OceanBase 数据库特有的超时参数,OceanBase 数据库还兼容了 MySQL 的超时参数,能够参考 MySQL 超时文档,MySQL 的超时参数会导致断连贯,有三个参数。

  • wait_timeout:闲暇 session 工夫,默认为 8 个小时,超过该工夫未发送任何 SQL 就会断连。
  • net_read_timeout:期待从连贯上读取数据的超时工夫。超时会导致连贯敞开。
  • net_write_timeout:期待从连贯上写入数据的超时工夫。超时会导致连贯敞开。

因为大部分超时机制触发后会间接敞开连贯,并且不打印超时日志,所以不理解超时机制的话很难确定 TCP 断连的起因,但理解后通过观察执行工夫或调整参数根本就能确定问题。OBProxy 作为代理层,并没有减少超时机制,不会让机制更加简单,同时,OBProxy 会感知数据库超时参数的设置,让整体体现合乎超时机制。

 

延长知识点 2:罕用网络工具

在很多时候咱们须要理解零碎网络行为,从而更好地解决问题。比方,在 OBProxy 日志中呈现了 Connect Error,咱们就能够通过 ping 命令进一步证实两台机器之间网络不通。接下来为你介绍一些罕用的网络工具。

ping 和 telnet 命令

ping 和 telnet 命令简略好用,ping 命令能够确定两台机器之间的网络是否连通,并且能够理解网络之间的提早,这对解决很多问题都十分有用。比方,提早几十毫秒,断定为路由到了其余城市的机房,这在 oltp 业务中是不合乎预期的。telnet 命令能够进一步确定机器端口是否在监听,从而发现服务程序未启动、程序 core 掉等问题。

抓包工具

当问题波及多个模块的协定交互时,只剖析日志无奈取得更多有用信息,此时能够应用 tcpdump 工具获取网络真实情况。举个例子,如果在 OBProxy 日志看到数据库执行 SQL 慢,而在 OBServer 看到 SQL 执行很快,此时就会狐疑网络问题,只有依据 TCP 报文能力确定是否是网络问题。
规范部署中,OBProxy 的端口是 2881,OBServer 的端口是 2883,如果抓 OBProxy 和 OBServer 的交互报文,能够在 OBProxy 的机器执行:

sudo tcpdump -i any port 2881 -w xxx.cap

TCP 报文就会保留在 xxx.cap 中。失去该文件拷贝到本地机器应用 wireshark 软件剖析即可。
除了下面介绍的命令,还有 ifconfig、ss、lsof、curl 等命令都十分有用,网络上材料很多,此处就不作更多介绍了。
总之,在分布式系统中,网络问题很常见。理解背地的起因,须要对操作系统、数据库系统、网络工具都十分相熟,因而减少了判断难度。上述内容对这些知识点都做了具体介绍,帮忙你更好地理解分布式数据库系统。

 

延长知识点 3:连贯治理的常见问题

尽管连贯治理的知识点很多,但当呈现连贯问题后,客户端报错根本都是 Communications link failure。这里关键点就是通过“要害信息”将 TCP 的两端分割起来,取得更多有用信息去进一步剖析。如“要害信息”是 SQL 和工夫点,那么就能够去 obproxy_error.log 中去查看,找到日志记录后,能够依据本系列第三篇文章介绍的内容去进一步排查。
这里咱们举两个例子阐明。例子 1 客户端报错:

--- Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
The last packet successfully received from the server was 70,810 milliseconds ago. The last packet sent successfully to the server was 5,005 milliseconds ago.

剖析报错信息,能够看到客户端上次从服务端收到申请是 70810ms 之前,客户端上次往服务端发送数据是 5005ms(5s)之前。这是在执行 SQL 过程中呈现了问题,个别是 Java 自身设置了 sockettimeout=5s 导致的,这能够向利用开发者确认。
例子 2 客户端报错:

--- Cause: com.alipay.oceanbase.obproxy.druid.pool.GetConnectionTimeoutException: get connection timeout, maxWait=500ms, maxCount=10, currentCount=11,

能够看到这是个 Druid 连接池的报错,获取连贯超时。此时 SQL 还没有执行,但从连接池曾经拿不到连贯了,问题起因就比较复杂。个别起因有:

  • 连接池自身配置连接数太少不够用,参考 maxCount 的值,个别调大能够解决。
  • 有慢 SQL 导致连贯没有及时偿还,这种问题就须要排查哪些 SQL 执行慢。

可见报错只是一种表象,很难一下就确定根本原因,须要收集更多信息去深刻排查。

 

总结

连贯治理波及的综合知识点多,波及的模块(包含驱动、OBProxy 和 OBServer)也多,因而变得复杂,但这也是微服务、分布式数据库的独特“痛点”。本文咱们从用户登录讲起,别离介绍了连贯治理、网络技术和常见问题,将相干原理和实际介绍给你。OBProxy 经验多年实际,解决了很多连贯治理的问题,n 也欢送大家学习交换。

 

课后互动

上期互动答案

小明想查看所有通过 OBProxy 的 SQL,请问有什么方法?
答:obproxy_digest.log 是审计日志,记录了所有执行工夫大于 query_digest_time_threshold 的 SQL,因而能够批改 query_digest_time_threshold 的值为 1us,就能够在 obproxy_digest.log 中看到所有通过 OBProxy 的日志了。

 

本期互动问题

JDBC 和 OBProxy 建设了一个连贯,早晨 OBProxy 被 kill 掉重启,请问此时 JDBC 会抛出异样吗?
欢送你在问答区发帖探讨,下篇文章揭晓答案。

正文完
 0