![f8f0743d713f7b42780d0e5e60c4eb0e.png](https://i-blog.csdnimg.cn/blog_migrate/1dfb1653e77cc30b29a1baa6a08ad775.jpeg)
OB君:好消息!我们将推出 分布式数据库OceanBase产品模块原理简介系列内容,通过完整13篇文章帮助数据库从业者建立更系统完善的数据库知识体系。第五期我们为大家带来分布式数据库OceanBase的通信协议层的完整介绍。更多内容敬请期待!
![2f615d5f0800515e4bdfcc6020a05b40.png](https://i-blog.csdnimg.cn/blog_migrate/a77b28ae77cbb9a4a3a67bfd740ed911.jpeg)
Part 1 前言
OceanBase是由阿里巴巴和蚂蚁金服完全自主研发的分布式关系型数据库,拥有极致的在线伸缩和负载均衡、高可用和强一致的技术能力。在使用OceanBase时,除了关注SQL功能和性能之外,理解OceanBase的通信协议层也至关重要。
OceanBase通信协议层是数据库内核和客户端应用的桥梁。这个桥梁的设计就是通用,是客户端无需为了OceanBase做改动而可以直接访问。OceanBase同时兼容Oracle模式和MySQL模式的2.1版本,本文将系统性的介绍客户端如何使用和区分各种模式。
OceanBase作为分布式数据库,不同于传统的单机或主备数据库。在通信协议层上,首要的区别是节点间的通信,传统数据库大多不设计节点间的通信。客户端方面,OceanBase同时兼容MySQL和Oracle租户模式是一大特色和亮点。和传统数据库最大的不同是,数据库是分布式,副本的主被打散分布在不同的Server上,因此在客户端访问OceanBase时,也和传统数据库不同。
OceanBase作为分布式数据库在通信协议实现上更加复杂,但是对于客户端而言,所有的复杂被完美的通过OBProxy和OceanBase内核包装起来,可以像访问传统数据库一样访问分布式数据库。如何做到像访问传统数据库一样访问分布式数据库?待本文为你逐一解答。
Part 2 框架介绍
OceanBase的通信协议主要包含两个方面:与客户端的通信,以及集群内部的通信。同时依赖于网络通信底层的Libeasy库。OceanBase兼容MySQL协议,内部实现为ObMysql。集群内部通信方式为RPC。
![989bf029218351ccaa15365163f97f3a.png](https://i-blog.csdnimg.cn/blog_migrate/1339621254e9f95b796e53b7c561a7d4.png)
OceanBase网络传输框架—Libeasy应用和协议层透过网络和其他的客户端或者服务器通信,那么需要一个Listener来监听网络特定网络端口的请求,同时请求处理后需要将结果response通过端口发送给客户端和服务器。这个过程是不确定的,比如客户端可能一直没有发送请求,那么实现Listener监听端口的线程,要么轮循查询是否有新请求(效率低,浪费CPU时间片)要么选择消息通知的方式。消息通知的方式就需要多个线程(任务)来接收对应的消息。目前最好的方式是通过系统 API epoll()来注册感兴趣和关心的事件,然后等待通知,通知收到后知道事件发生,同时也了解到事件发生的具体队列,可以直接去队列取数据。在此基础上的标准事件库可以大大简化代码量并提高效率。比如Libev/Libevent等等。
Libev是Linux上的事件驱动框架— POSIX event Libray,http://software.schmorp.de/pkg/libev.html。Libev的目的就是实现对不同操作系统API层的封装,提供一种框架来实现多路复用和异步I/O。
在说通信协议之前首先要明确OceanBase的通信是构建在Libeasy库之上的,Libeasy是构建在libev库之上的,对异步I/O, 网络事件进行了更好的封装。Libeasy是shared nothing的库,同时使用协程来管理任务调度,减少线程用户模式到内核模式的context switch非常的高效。所以它很适合与分布式结合。基本上网络框架都是基于callback的方式。在此不作展开,可以参考libev或者netty的设计。都是通过事件触发处理的逻辑,将网络收到的数据从内核读到用户空间,将要发送的数据从用户空间拷到内核空间。
OceanBase客户端通信
- 客户端通信架构
OceanBase支持MySQL的通信协议。MySQL通信协议是开源的,文档可以参考Oracle官网的链接——https://dev.mysql.com/doc/internals/en/client-server-protocol.html。在此文不作过多的涉入。OceanBase是原生的分布式数据库, 多副本采用Paxos协议形成一个Leader多个Follower的设计。数据副本按照Leader、Follower方式分布在多台服务器上。客户端写/强读操作需要访问分区副本的Leader, 为了实现客户端读写访问透明,需要路由和反向代理层解决这个问题。OceanBase采用并推荐OBProxy作为反向路由和转发的服务。
![3011a7d07b2760f1beac116e6dfdfec5.png](https://i-blog.csdnimg.cn/blog_migrate/2deb75b9bf78bf6d19b57dee871e26d1.jpeg)
- OBProxy
OBProxy作为OceanBase的的反向代理服务器,具有防连接闪断、OBServer宕机或升级不影响客户端正常请求、兼容所有MySQL客户端、支持OceanBaseOracle客户端、支持热升级和多集群等功能。OBProxy 核心功能:路由OBProxy核心功能路由是通过解析客户端SQL报文,同时结合规则(是否设置读zone优先级,是否和OBServer同城,需要路由的Server是否在合并,是否读写分离部署等)来选择最佳的Server进程路由和转发。具体流程:
1)解析SQL。OBProxy内部可以解析SQL,但是仅仅需要解析出SQL的Hint,数据库名,表名等即可。
2)解析后根据规则来路由。使用默认的路由规则有:LDC优先,Leader优先。
3)获取路由表。 路由表是SQL涉及Partition的目标Server集合。
4)选择目标Server。
4)黑名单过滤。
5)最后转发Request。
举例说明:select gmt_create, id, name from student_list where id > 100OBProxy的路由方式首先解析这是读动作,作用在表student_list上,基于规则检查是否有租户/session级别的弱读设置,如果没有,确认为强一致读,将请求转发给student_list表分区Leader所在的Server。
OBProxy路由应对变化的方式:通过OBServer反馈OBProxy有本地缓存同时依赖内部表。但是OceanBase集群的状态是会变化的。一旦发生变化,原来的路由方式就是不正确的,那么OBProxy如何感知呢?方法是在路由发生错误时,OBServer端执行后,在返回结果的OK数据包中,包括了额外信息:partition hit,系统内存是否超限,是否在初始化,是否在退出,是否包校验出错等。
通过 Partition hit来发现此次路由是否正确。比如强一致读的动作,如果Partition Leader切主(Leader切换到另外一台Server),如果仍然按照之前的规则路由是没有路由到Partition Leader所在的Server。OBServer此次进行远程执行,然后通过在结果包,或者错误包后额外返回一个OK包来反馈信息,这使得使OBProxy可以更新location cache的信息,保证后续路由的正确性。
![cd302f2db7818805db621ea1c41e5704.png](https://i-blog.csdnimg.cn/blog_migrate/330fe6945ab5a1a9852759475cd5d2bc.png)
- 客户端通信时的应用层校验
OceanBase 1.x版本在实现MySQL通信协议时,始终考虑到数据的正确性。虽然应用层下的链路、网络、传输层都有通信校验功能。但是由于未知bug的存在(比如操作系统内核bug),无法在应用层100%保证下层的校验。所以,为了实现应用层的checksum而采用压缩方式,同时考虑性能损耗,采用了最低级别的压缩方式,压缩方式使用的是MySQL zlib。
而无论哪种级别的压缩方式都会在尾部添加4字节的alder32校验值。这是zlib压缩方式决定的,压缩级别为level=0。为什么采用压缩的方式通信并且又选择了压缩级别最低的0(最低级别,不压缩;最高级别9;默认级别6)。因为,首先压缩过程会对CPU有压力,实测结果显示如果使用默认压缩级别6,那么QPS会受到明显的影响。然而,如果不选择压缩方式,那么无法在应用层对数据进行校验。
![9b34b7b26b43dfa0018280d983e7d429.png](https://i-blog.csdnimg.cn/blog_migrate/a4730fde3ea258fffe58fc74d74bb484.jpeg)
这样的应用层校验设计满足了OBProxy到OceanBase数据库连接的数据正确性。而客户端到OBProxy一般是部署在应用端,不走网络。如果OBProxy和客户端不在同一机器上而是跨了网络,那么需要客户端开启压缩协议后才支持校验。开启方式是在连接建立后的Capability Negotiation时确定的。
OceanBase 2.1版本以后,考虑到MySQL协议压缩算法会带来的性能损耗。并且新版本同时支持MySQL模式和Oracle模式,仅仅是MySQL的通信协议实现无法满足需求。因此采用另外的解决方法来进行应用层校验。大体上的做法是将标准的MySQL报文包装起来,在头部添加包头的校验位和其他防攻击机制同时包含租户兼容新信息以及各种标识位。而在包尾端添加Tailer,Checksum工作是在Tailer中进行的,采用CRC校验方式。这样的好处是避免了压缩算法进行checksum对CPU的损耗,同时也顾全了应用层的校验保证了数据正确性。
OceanBase内部通信
OceanBase内部通信RPC—除了客户端的通信外,OceanBase原生分布式的特点是和单机数据库的最大不同。主要因为单机数据库多进程之间在同一台服务器上运行。单机数据库内部通信方式可以根据操作系统提供的进程间通信的特性来选择,比如IPC, Message Queue, Shared Memory等等。分布式多机的通信是OceanBase必须要解决的难题之一。在既要保证正确性,又要在保证性能的同时考虑网络带宽的压力。分布式集群多机间的通信无时不在,比如集群间心跳包。比如服务器夯机后的处理,副本leader的改选。比如扩容服务器,RootService对unit做的负载均衡。这些需求可能是频繁的很小的包,也可能是大量的数据在机器间传输的包。RPC是必然的,RPC的特性决定了她在分布式系统中不可替代的地位。OceanBase的RPC设计,支持流式和异步。流式RPC可以支持大量数据的传输,允许在一个RPC请求上多次获取结果。异步RPC中包含回调函数,通常用于时间长或不确定时间的处理,避免等待。OceanBase上RPC典型的应用场景为:
- DDL操作
- Paxos选举
- 日志同步
- RootService管理,负载均衡以及各Server通信
- 分布式事务中间结果处理
选取几个典型的优化场景来看RPC方面的优化。Paxos选举的情况是打散分区选举的时间,比如单机2万分区,如果2万分区同时开始选举对会对网络产生很大压力。优化的方式是采用multi-paxos的方式减少1次RPC的通信延迟,又通过将选举时间打散在选举周期的时间轴上的方式来将选举数据包打散。
另外对网络带宽压力大的情况是日志同步,为此特意扩展了RPC的属性,支持日志文件同步RPC时指定压缩。这样可以用一点CPU的时间换取网络的资源。如需开启压缩,可以通过设定系统参数clog_transport_compress_all。DDL操作由RootService同步/异步通过RPC到其他所有服务器上刷新schema,使集群schema版本一致保持最新。
Part 3 Oracle租户模式
OceanBase 2.1版本支持了Oracle租户模式。首先面临的问题是MySQL和Oracle在数据类型上的差异。以及Oracle通信协议并没有公开的困境。同时MySQL和Oracle存在许多不同,比如,数据类型区别,同数据类型长度区别,错误返回值,大小写敏感,某些语法等。如何在实现了MySQL兼容模式的情况下兼容Oracle模式?OceanBase的实现方案是分MySQL租户和Oracle租户。租户一旦设定不能动态改变兼容性。租户的兼容性通过schema持久化到租户所在的Server。如需切换租户,需要重新建立连接。而建立新的连接时需要指定租户类型。租户类型始终伴随整个连接的过程。租户类型确定后,语法,数据类型,和操作等也随之确定,如果语法和租户冲突,返回报错。内部表实现仍然采用MySQL兼容模式,Oracle兼容模式租户读取内部表实际是读取Oracle虚拟表。虚拟表内部做了两种模式的映射。
比如:select gmt_create, id, name from student_list limit 10;如果租户模式为Oracle,那么上述语句语法错误。Oracle使用rownum来限制行数。整个连接过程是客户端连接选择Oracle租户。然后上述语句通过OBProxy解析,注意OBProxy不会认为这个非法。它仅仅将语句转发到对应的Server上由Server来执行。这样可以保证OBProxy的轻量化。在OBServer端解析语句时发现语法和租户模式的冲突返回报错。
OceanBase有客户端驱动来帮助客户端程序忽略协议细节。然后,在原有支持MySQL协议的基础上做扩展。在兼容Oracle租户模式的同时,MySQL租户模式同样支持原生MySQL协议,简单举例,如果客户端仅仅使用MySQL租户模式,那么,仍然支持MySQL协议,整个链路完全透明做到应用无感知。同时支持所有MySQL客户端驱动。而如果使用Oracle租户模式,或者两种模式都有使用,可以通过Client端驱动来配合使用。Client端驱动就是将扩展的通信协议实现封装用以实现对客户端透明。驱动包括JDBC, Obclient(C/C++)。
比如JDBC驱动完全兼容MySQL JDBC的使用方式,并且可以自动识别模式是MySQL还是Oracle。这个自动识别也是将租户兼容性信息添加到扩展的OK包中。 同时,对于数据库的管理,测试等可以通过OBClient(OceanBase Client)直接连接数据库,然后在连接时选择使用哪个模式。客户端应用程序,通过各种驱动同样避免了应用的改造可以忽略SQL通信协议层的实现。
Part 4 运维最佳实践
MySQL, RPC 的端口好可以在启动时更改,也可以通过系统参数更改:
mysql_port / rpc_portRPC运维实践:性能参数:clog_transport_compress_all (false)如果开启该参数,Clog同步传输时采用压缩方式。需要根据网络带宽以及CPU压力来决定。如果CPU压力很大,网络带宽仍然充足,可以关闭该参数,释放部分CPU资源。OBProxy运维实践:
- 错误:ERROR 1203 (42000): Too many sessions
OBProxy 默认支持8192连接,如果连接请求压力大,可以调整参数 client_max_connections 改参数值范围【0,65535】默认8192。 - 日志:日志记录信息 obproxy's memroy is out of limit's 90%
解决方法:业务当前压力是否正常范围,同时考虑调整OBProxy内存上限 proxy_mem_limited。Proxy_mem_limited 默认800MB, 可以调整为2GB。 - 日志:日志记录信息 failed to accept con(ret=-10024)
解决方法:这个报错是Socket accept 错误代码10024 表示:Too many open files。可以通过增加运行OBProxy操作系统层的ulimit -n来增加允许打开的文件句柄数。如果仍然有报错,需要查看是否有很多连接没有被关闭。导致句柄无法释放。
RPC和MySQL数据包统计监控:
统计监控:无论是MySQL的报文数量还是RPC的流量都可以通过内部视图来查看gv$sysstat。
Part 5 总结
最后,在以上对通信协议层的简介过程中,我们可以看到OceanBase在兼容性方面做的工作,在保证数据正确的前提下,提高性能,同时为兼容性考虑。OceanBase的目标就是通用数据库。另外,性能优化是非常困难的,比如安全和性能在很多情况下是矛盾体。有时在有限的资源下不得不牺牲部分性能来换取更加安全的数据传输。而有时又要基于业务特点在CPU、内存、网络之间寻找平衡。
<完>
加入OceanBase技术交流群:
— 想了解更多OceanBase背后的技术秘密?
— 想与蚂蚁金服OceanBase的技术专家深入交流?
— 扫码进入OceanBase钉钉交流群
![aa3a10b6ea0192207b61d8b416b4403b.png](https://i-blog.csdnimg.cn/blog_migrate/9fe2be20fb145d9188f3e47d6458ccb7.jpeg)
往期推荐:
● 最系统!一篇文章读懂OceanBase数据库的产品家族体系
● OceanBase SQL 引擎的模块介绍和调优实践分享
● 一文详解 OceanBase 并行执行引擎实现
● 十年磨一剑!OceanBase查询优化器的设计之道和工程实践哲学
● 100%自主知识产权!蚂蚁金服自研数据库OceanBase的设计与实践哲学