摘要:OBProxy是OceanBase专用的反向代理服务器,从2015年开始设计编码发布,目前已经伴随着OceanBase稳定支撑阿里/蚂蚁内部上百个关键业务,以及多家外部客户。本文将从Why、What、How角度深入解析OBProxy。
本文作者
严华,现任蚂蚁金服OceanBase团队高级开发工程师,2015年加入OceanBase后一直从事数据链路/SQL引擎方面的研发工作。
OceanBase作为典型的分布式关系数据库,从产品架构层面可以分为以下三部分:
- 服务端产品:由若干分布式ObServer组成,均包含SQL引擎/存储引擎/分布式事务引擎,共同配合提供一个高可用、可扩展的分布式关系数据库系统。
- 数据链路产品:提供从用户端到数据库端的最佳链路访问功能,屏蔽用户对分布式数据库的感知,保障分布式数据库的最高性能服务。
- 云管控平台:提供OceanBase云产品服务,简化在使用OceanBase过程中的运维和业务开发复杂度,做到轻松上云。
其中, 数据链路的核心产品是就是本文的主角——OBProxy。OBProxy作为OceanBase专用的反向代理服务器,从2015年开始设计编码发布,经过蚂蚁金服内外部业务的多年打磨,目前已经伴随着OceanBase稳定支撑阿里/蚂蚁内部上百个关键业务,以及浙商银行、南京银行、PayTM等多个外部金融客户。本文将从Why、What、How角度分别讲解OBProxy,希望帮助大家借此加强对OceanBase的架构思想和高性能原理的理解。
为什么要OBProxy?
分布式关系数据库与单机数据库最大的不同在于数据存储在多个节点,并不会因为单节点的异常导致整体的数据丢失。然而用户的使用方式还是不变,还是期望的client <--> database
。分布式数据库中每个server都能提供服务,那么用户该访问哪个server呢?由于数据是分布式存储,用户的DML请求发给不同的server执行的性能必然有所差异,用户的请求该发往哪个server呢? 假如分布式的多个server中宕机一台server,那如何不让用户受到影响影响呢?针对这些分布式数据库都会遇到的问题,各个产品的解法各不一样。
Google的F1分布式关系数据库采用用户 <--> F1 client <--> 负载均衡组件 <--> F1 server <--> Spanner <--> CFS
的数据链路。如上图,用户通过F1 client发出请求,请求会首先根据负载均衡组件发给最优的F1节点上的F1 server,F1的Spanner节点中的大部分F1 server是无状态SQL引擎,不存储数据,而是依赖Spanner从CFS中读取的数据。Spanner将数据多副本方式存储, 可以指定版本数据的读取, 但是写操作需要在leader serevr执行. 因此实际中F1 client + 负载均衡组件 + F1 server + Spanner
共同负责承担路由和容灾的功能,即要保证client访问的是最优的链路的F1 server和Spanner,同时F1 server和Spanner要具备容灾能力[1]。
基于分库分表+MySQL实现的分布式关系数据库采用用户 <--> MySQL驱动 <--> 负载均衡组件 <--> 中间件 <--> MySQL
的数据链路。如上图,用户通过各类MySQL驱动发送请求,经过负载均衡组件路由到任意一个的中间件上,中间件根据分库分表规则将用户请求拆分发到各个分布式的MySQL上, 每个MySQL执行自己分配的SQL,中间件最后负责将结果汇总组合返回给用户。MySQL由主备server组成,其中一台server的宕机不影响MySQL整体服务,一台中间件的宕机不影响负载均衡组件路由到其他无状态的中间件上。因此这里由负载均衡组件 + 中间件完成路由和容灾功能。
当前OceanBase采用的数据链路是用户 <--> MySQL驱动 <--> 负载均衡组件 <--> OBProxy <--> OceanBase
。如上图,其中用户通过任意MySQL驱动发出请求,请求通过负载均衡组件访问到任意一台无状态的OBProxy上,OBProxy将用户请求转发到后端OceanBase集群中最佳的ObServer去执行。这里负载均衡组件可以是市场上常见的产品如SLB,OBProxy不负责分库分表,也不作为SQL引擎参与执行计划的生成调度,只负责纯粹的反向代理转发。每个ObServer均包含完整SQL引擎和存储引擎,负责解析用户SQL生成物理执行计划并执行,分布式的ObServer之间通过Paxos协议保证高可用。这种架构设计中,OBProxy只承担基本的路由和容灾功能,而数据库的功能全部交由ObServer实现。这样更加简单明确的分工可以各组件性能做的更加极致,OceanBase整体最高也能做到访问单机数据库的性能。
为什么OBProxy不能使用市场上其他DB Proxy代替呢? 因为市场上通用DB Proxy基本是面向MySQL Server设计的,并不适用分布式的OceanBase。为OceanBase量身定制的OBProxy能够对非分区表/分区表的路由更加准确,能和observer的配合更加完美。其次,定制化的OBProxy可以提供对更多OceanBase集群支持,以及便利运维OBProxy的支持。最后,定制化的OBProxy可以站在用户的角度做跟多实用的优化,方便用户使用OceanBase。
OBProxy是什么?
OBPrxoy是为OceanBase数据库专门量身定制的反向代理服务器,用户的数据在OceanBase上以多副本的形式存放在各个ObServer上, OBProxy负责接收用户发过来的SQL请求,转发用户请求SQL路由到最佳目标ObServer上,并将执行结果给用户。作为OceanBase数据库产品不可或取的一部分,OBProxy具备以下特性:
- 高性能转发: OBProxy完整兼容MySQL协议,采用多线程异步框架和透明流式转发的设计,保证了数据的高性能转发(单核5万QPS),以及自身对机器资源的最小消耗(内存不超过50M,单cpu不超过20%)。
- 最佳路由: OBProxy需要充分考虑用户请求涉及的的副本位置、用户配置的读写分离路由策略、OceanBase多地部署的最优链路,以及OceanBase各机器的状态以及负载情况,将用户请求路由到最佳的ObServer,最大限度的保证OceanBase整体性的高性能运转。
- 连接管理:针对一个Client端的物理连接,OBProxy维持自身到后端多个ObServer的连接,采用基于版本号的增量同步方案维持每个ObServer的连接在同一状态,保证了Client高效访问各个ObServer。
- 定制协议:原生MySQL协议存在报文CRC校验缺失,Request ID校验缺失的正确性缺陷,而OBProxy默认使用OceanBase定制协议予以解决,保证了和ObServer之间链路的正确性。
- 易运维:OBProxy本身无状态,支持同时访问多个OceanBase集群,支持无限水平扩展。并且可以通过丰富的内部命令实现对自身状态的实时监控,实现自身冷热升级/下线/重启等常见运维操作,可以提供极大的运维便利性。
尽管OBProxy已经在代理转发上做的很极致,但是Client <--> OBProxy <--> ObServer
始终是要经过两次网络延迟开销。为了最小化解决这个问题,在实际高性能要求的场景中,可以选择将OBProxy部署在Client端所在机器上,由守护进程保证OBProxy进程的高可用,Client到ObServer实际只有最短一跳网络路径,这样用户可以通过访问本地的OBProxy来实现对远端分布式OceanBase数据库服务的访问。OBProxy自身运行非常小的资源消耗也能保证这一部署的可行。
另外,大家肯定很好奇,其他数据库的数据链路产品通常都支持分库分表/SQL审计等中间件功能,为什么OBProxy不支持?
其实分库分表只是传统数据库解决单一数据库并发访问性能不足和存储瓶颈问题的一种常用扩展手段,传统数据库依赖数据链路产品的分库分表中间件逻辑来帮助实现高性能的数据库服务,而OceanBase并不依赖这个。OceanBase通过一级分区/二级分区的分区表的手段解决上述问题,而OBProxy也不必改写用户请求SQL,只需要根据用户的请求SQL计算出所涉及的分区的目标ObServer地址,然后透明转发代理。
另外,根据OceanBase的架构设计,OBProxy只需专心做代理,SQL功能交给后端ObServer支持。并且OBProxy后端对应的ObServer都属于同一个OceanBase集群,并不是其他数据库相互独立的数据库实例。因此其他中间件的SQL审计等SQL层功能只需要OceanBase本身支持,不需要依靠数据链路产品单独实现。
OBProxy是如何设计实现的?
OBProxy的主要架构可以简单描述如上图所示,其中异步框架实现高效的代理转发,通信协议实现OBProxy与Client和ObServer之间通信方式,连接管理实现OBProxy的连接池功能,路由选择实现对用户请求的的最优路由选择,而容灾模块则负责监控OceanBase集群状态。监控运维提供对OBProxy的丰富运维手段,集群管理实现OBProxy对OceanBase多集群的支持。以上组件相互依赖配合,共同实现OBProxy的整体功能。
下面选取OBProxy比较核心的组件进行详细说明。
异步框架
多线程的异步框架是OBProxy的核心组件,包括事件处理,网络包处理,转发流程三部分功能。基于状态机模型的设计,也使得OBProxy整个转发流程更加流式高效。
OBProxy在启动时,会根据当前机器的CPU核数创建一组工作线程,每个工作线程上运行着独立的异步事件处理程序。Client <--> OBProxy
的一个物理连接在连接之后,会被分配到一个OBProxy工作线程上执行直到该连接生命期结束。而对于该Client连接,OBProxy按需维持了多个 OBProxy <--> Observer
的server连接,这些连接亦绑定在相同的工作线程上。由于OBProxy和ObServer均完整兼容MySQL协议,因此Client端发给OBProxy的请求,OBProxy根据路由选择模块确定最佳ObServer后,直接将该网络报文转发的ObServer,并将ObServer的回包转发给Client端,整个流程中除了Client->OBProxy
和ObServer->OBProxy
的的两次网络报文读取,以及OBProxy->ObServer
和OBProxy->Client
的两次网络报文写入之外,没有其他IO开销,也没有该报文在内存中来回拷贝的开销,因此这样做到了真正的透明流式转发。
至于流量控制问题,OBProxy采用生产者消费者模型,以ObServer生产数据,Client消费数据为例,数据流向可以描述为:网络producer(ObServer)—>网络consumer(Client)
, 相当于一个consumer和一个producer共用一个buffer,这个buffer如果超过40KB,就不从网络上读取数据。即假如consumer消费慢,producer生产快,一旦共用的buffer超过40KB,OBProxy不允许再生产数据,直到consumer消费掉,OBProxy再允许producer继续生产数据。也就是说,用户的一次查询,OBProxy最多hold 40KB的数据,并不会存在常见的流量异常而导致OBProxy内存占用过大的问题。
路由选择
路由选择是OBProxy的核心功能,如前所述说,路由选择的输入用户的SQL,用户配置规则,ObServer的状态,路由选择的输出是一个可用ObServer地址。其路由逻辑可以入下图所示:
其中,解析SQL模块使用OBProxy自己定制的语法parser模块,只需要解析出DML语句中数据库名/表名/hint,不需要其他复杂的表达式推演等,因此parser模块做的十分高效。
路由规则确定模块中,OBProxy需要根据不同情况确定最佳的路由规则。比如强一致性读的DML请求期望发到副本leader的ObServer上,弱读的DML请求和其他请求则不要求,leader和follower均衡负载即可。如果OceanBase集群是多地部署,OBProxy还提供了LDC路由,优先发给同机房的ObServer,其次是同城的ObServer,最后才是其他城市的ObServer。如果OceanBase集群是读写分离部署,OBProxy还需要提供读zone优先,只限读zone,非合并优先等规则供业务按照自身特点配置。上述的几种情况在路由选择中是组合关系,输出是一个确定的路由规则。
获取路由表是指OBProxy根据用户的请求SQL获取该SQL涉及的副本位置。OBProxy每次首先会尝试从本地线程缓存中获取路由表,其次是全局缓存,最后才是发起异步任务去向ObServer查询路由表。对于路由表的更新,OBProxy采用触发更新机制。OBProxy每次根据路由表转发给ObServer的请求,当ObServer不能本地执行时,会在回包时反馈给OBProxy。OBProxy根据反馈决定是否下次强制更新本地缓存路由表。那么什么时候路由表才会变化呢?通常是在ObServer合并或者负载均衡导致切主发生才会发生。
选择目标ObServer则是根据上上一步确定的路由规则从上一步获取的路由表中选择最佳的ObServer,在经过黑名单/灰名单检查通过后作为最终的目标server进行请求转发。ObProxy的容灾机制是通过黑名单/灰名单实现,下面单独说明。
容灾
OBProxy的容灾策略主要影响路由选择和连接管理,主要包含黑名单和灰名单两种检查,用于处理ObServer错峰合并、升级、宕机、启动/停止,网络分区等状态。黑名单采取定期刷新维护,由ObServer反馈哪些server节点死不可用。灰名单采取主动触发维护,当OBProxy转发请求给ObServer,如果发现ObServer返回特定的系统错误,或者ObSerer在N秒内有M次连续不可用,则将该ObServer加入灰名单。黑名单中的ObServer将被过滤不访问,但是灰名单中的ObServer每隔一段时间会重试一次,检查是否需要洗白,以避免长时间将ObServer拉黑。
对于用户的连接,通常情况下是Client <--> OBProxy <--> 多个Observer
。而在Client的一个事务中,只会使用到一个OBProxy <--> ObServer
的连接。对于其他非事务中的连接,如果ObServer发生异常导致网络连接断掉,OBProxy会关闭该无效的连接,Client端将对此无感知不受影响。
总结
OBProxy作为OceanBase的专用反向代理服务器,是OceanBase数据库能够最高性能服务用户必备组件,也是目前OceanBase外部市场输出的标配。尽管OBProxy已经十分成熟稳定,但在横向的外部市场开拓上,OBProxy作为数据库“中间件”,自身还可以做的更多;在纵向的数据库研发上,OBProxy作为数据链路的一环,发展还很长。这里也欢迎有志之士前来勾搭,共同为OceanBase的壮大贡献最好的力量!
参考资料:[1] F1: A Distributed SQL Database That Scales