上篇文章咱们介绍了 OBProxy 的连贯治理,通过连贯治理性能,OBProxy 和 OBServer 分割起来,同时 OBProxy 屏蔽了连贯的复杂性,让用户应用起来和单机数据库一样简略。实现接入后,接下来的一个重要性能就是数据路由,这也是大部分用户最关怀的性能之一,本文会对其进行具体介绍。

在介绍 OBProxy 的路由原理前,咱们先探讨下路由须要思考的影响因素,不便你更好地了解前面的内容,以及评估一个路由性能的好坏。我将从性能、性能和高可用三个因素开展介绍。

数据路由影响因素

性能因素

你是否思考过,一个单机数据库的性能如 MySQL 数据库的 Prepared Statements 性能,在分布式系统中该如何实现,比方 PREPARE Statement 和 EXECUTE Statement 该发往哪个节点?

咱们以 Prepared Statements 性能为例阐明性能对路由的影响。Prepared Statements 执行次要有两个步骤:

  • 步骤一:执行 PREPARE 操作,如发送SQL select * from t1 where c1 = ?
  • 步骤二:执行 EXECUTE 操作,传递 select 语句应用的数据,并执行 select

步骤二是依赖于步骤一的,咱们假如执行状况如图1所示,你就会明确,当 OBServer2 收到步骤二( EXECUTE)的申请后,并不知道步骤一(PREPARE)申请的内容,这时,OBServer2 就会报错(优雅做法)或断连贯(粗犷做法)。


图1 PS性能执行状况

对于这种问题,常见的解决办法有两种(见图2)。

  • 办法一:记录 PREPARE 路由到的节点 OBSERVER1,EXECUTE 申请持续路由到 OBServer1;该办法实现简略,但无奈施展分布式系统的劣势。
  • 办法二:在执行 EXECUTE 前,将 PREPARE 的状态同步给 OBServer2,参考连贯治理局部的状态同步;该办法须要 OBProxy 同步连贯状态,实现简单,但能够利用分布式系统的劣势,目前 OBProxy 采纳了该办法。

图2 解决路由失败的两个办法

性能因素

高性能是 OceanBase 数据库的重要个性,路由对性能的影响次要是提早,即网络通信开销。OBProxy 通过感知数据分布和机器地理位置升高网络通信开销,进步整体性能。

数据分布次要影响执行链路的跳数,OBProxy 路由时间接命中数据所在的节点是最好的。咱们以 SQL 语句 select c1 from test为例阐明数据分布对性能的影响。如图3所示,t1表的数据分布在 OBServer1 下面,路由形式1间接路由到 OBServer1 ,效率最高;路由形式2发给了一个无t1表数据的 OBServer2,OBServer2 再进行路由转发给 OBServer1,相比形式1性能变差。为了实现路由形式1的路由,OBProxy 须要感知SQL和表数据分布,后文会具体介绍。图3 OBProxy 的两种路由形式

机器地理位置次要影响网络提早,当抉择了一个远端节点后,SQL执行会变慢,有时网络提早的工夫比数据库执行工夫要大很多。咱们在阿里云上做了测试,提早数据如下:

  • 杭州同可用区 rtt min/avg/max/mdev = 0.111/0.141/0.433/0.060 ms
  • 杭州不同可用区 rtt min/avg/max/mdev = 1.847/2.003/5.840/0.740 ms

能够看到跨可用区后提早减少靠近2ms,对于简略SQL,数据自身执行工夫可能才100us左右。因而,对于不同地理位置的机器,OBProxy 抉择优先级是:同机房>同城不同机房>不同城市。图4展现了优先选择同城市的OBServer1。图4 优先选择同城市的OBServer1

高可用因素

高可用因素是指 OceanBase 数据库对机器故障有容忍能力,让故障对利用通明无感知,OBProxy 发现 OBServer 节点故障后,路由时会排除故障节点,抉择衰弱节点,对于正在执行的SQL也有肯定的重试能力。高可用波及故障探测、黑名单机制、重试逻辑等内容。如图5所示,OBProxy 发现OBServer1 故障后,将该节点退出黑名单。路由时从衰弱节点抉择。
图5 OBProxy 发现 OBServer 节点故障的解决逻辑

理解了数据路由的影响因素和路由准则后,咱们就能够更高效地进行路由策略设计了。不过,现实情况会简单很多,原则上咱们要实时感知 OBServer 状态、数据分布等,但在工程实际中很难做到,便引发出许多问题。因而,咱们在思考路由时须要兼顾性能、性能和高可用,让 OceanBase 数据库“更好用”。

OBProxy 路由性能

咱们晓得通过 OBProxy能够拜访不同集群的不同租户的不同机器。这也是 OBProxy 能够实现集群路由、租户路由和租户内路由的起因,接下来我将围绕这三局部介绍OBProxy的路由性能。

集群路由

集群路由是指 OBProxy 路由性能反对拜访不同的集群,它的关键点在于获取集群名和rslist的映射关系(见图6):

  • 对于启动参数指定rslist的启动形式,集群名和rslist的映射关系通过启动参数指定
  • 对于指定config_server_url的启动形式,集群名和rslist的映射关系通过拜访url获取

须要留神的是,这里的rslist不须要蕴含所有的集群机器列表,OBProxy 会通过拜访外部表获取集群所有机器,个别rslist为RootServer(OceanBase的总控服务)所在的机器。图6 集群路由步骤

咱们从图6中能够看到,OCP 是集群路由时十分重要的一个模块。当呈现集群路由问题时,大部分都是 OCP 模块呈现了问题,常见等问题有两个。

  • OCP 服务不可用:OBProxy 无奈通过 OCP 获取集群名和rslist的映射关系,导致登录失败。
  • OCP 返回谬误的后果:如 OBProxy 通过HTTP协定拜访OCP,获取后果,后果为JSON格局,如果格局有问题,会导致后果解析失败。

OBProxy 是在用户登录首次拜访集群时获取rslist,并保留到内存中,后续再拜访该集群,从 OBProxy 的内存中获取就能够了。这里须要留神,当集群内存信息创立好后,OCP再呈现问题,即便OBProxy 仍能够失常工作,也要及时排查 OCP 问题。

租户路由

OceanBase 数据库中,一个集群有多个租户,租户路由是指 OBProxy 路由性能反对拜访不同的租户。在泛滥租户中,sys租户比拟非凡,相似于管理员租户,和集群治理相干。咱们将离开探讨sys租户路由和一般租户路由。

1. sys租户路由

实现集群路由后,咱们能够取得集群的rslist,此时 OBProxy 会通过[email protected]账号登录rslist中的一台机器,并通过外部表__all_virtual_proxy_server_stat获取集群的所有机器节点。在 OceanBase 数据库的现有实现中,sys在每个节点都有散布,因而,__all_virtual_proxy_server_stat返回的后果也就是sys租户的路由信息。

OBProxy 会15秒拜访一次__all_virtual_proxy_server_stat,保护最新的路由信息,这样集群产生节点变更都能够感知到。

除了集群机器列表,OBProxy 还会通过sys租户获取partition散布信息、zone信息、租户信息等。可见sys租户对 OBProxy 十分的重要。

2. 一般租户路由

sys租户的路由信息就是集群的机器列表,但一般租户不同,一般租户路由信息就是租户资源(unit是CPU、内存、磁盘等资源载体,详情可查阅 OceanBase 数据库名词概念)所在的机器。

留神:因为历史起因,查问租户路由信息并不是通过unit相干的表,而是通过非凡表名__all_dummy示意查问租户信息。OBProxy 须要通过外部表__all_virtual_proxy_schema获取租户的机器列表,在拜访__all_virtual_proxy_schema时,OBProxy 指定表名(__all_dummy)和指定租户名获取租户的节点信息。图7展现了租户的路由信息。_

图7 一般租户路由信息

当获取到租户信息后,OBProxy 会保留在本地内存中,并依据肯定策略进行缓存信息的更新。对于 sys 租户,通过15秒一次的拉取工作取得最新的信息;对于一般租户,而刷新频率并不高,一般租户的路由缓存策略如下。

  • 创立:首次拜访租户时,通过__all_virtual_proxy_schema取得一般租户路由信息并创立。
  • 淘汰:当 OBServer 返回错误码 OB_TENANT_NOT_IN_SERVER时设置缓存生效。
  • 更新:当缓存生效后从新拜访__all_virtual_proxy_schema取得一般租户路由信息。

总的来说,在多租户架构下,OBProxy 通过 sys 租户取得元数据信息,sys租户自身路由信息就是集群的机器列表,而后通过元数据信息取得租户的路由信息。通过租户路由性能,OBProxy 反对了 OceanBase 数据库的多租户架构。

租户内路由

租户内路由是指在获取租户的机器列表后,抉择适合的节点执行SQL。

对于租户内路由,OBProxy 能够像一般代理如 HAProxy 一样,一个客户端连贯对应一个服务端连贯,服务端机器从租户机器列表选取,这样性能就会简略很多,但无奈满足性能和高可用要求,可见路由影响了连贯治理。

租户内路由是路由性能最简单的局部,次要起因是在想方法提供更好的性能和更高的可用性。我将依照主正本路由、备正本路由、租户机器路由、缓存信息、路由策略、事务路由和常见问题这七局部为你介绍。为什么是这样的程序呢?你能够在浏览完本文后尝试梳理思维导图,感触更深。

1. 主正本路由

在分布式系统中,为了容灾高可用,会采纳多正本机制。正本之间须要保证数据一致性,往往采纳Paxos或者Raft算法,在工程实际中,有一个非凡正本,该正本数据最新,并控制数据在正本间的同步,这个正本叫做主正本,其它正本叫做备局部。

因为 OceanBase 数据库只有一个主正本,因而,主正本路由策略就是发往该正本。咱们以select c1 from t1为例,介绍主正本路由须要满足的两个条件:

  • SQL语句操作(查问、插入、更新和删除等)实体表,下面例子是t1表。
  • 申请必须读到最新数据,即强读(和弱读对应,弱读不要求读到最新数据)。

在 OBProxy 日志中,主正本路由的关键字是ROUTE_TYPE_LEADER。要实现主正本路由,就须要晓得拜访的分区标识和分区所在的地位。在 OBProxy 的实现中,分为两种状况。第一种状况是单分区表,表只有一个分区,依据表名就能够取得正本地位信息;第二种状况是多分区表,表有多个分区,OBProxy 须要依据SQL中的表名和分区键计算出分区标识,再获取正本地位信息。正本信息蕴含主正本信息和备正本信息,主正本路由只须要应用主正本信息。

对于多分区路由,波及分区形式(如hash、range和list)、分区键类型(number、varchar等)、分区算法(如hash算法)、类型转换(如SQL中的值类型和分区键类型不同)等知识点,实现比较复杂,咱们以二级分区为例介绍,分为10个步骤(见图8)。

图8 多分区路由流程

2. 备正本路由

理解完主正本路由策略后,你肯定想到了备正本路由。备正本路由也须要满足两个条件:SQL语句查问实体表,下面例子是t1表;申请要求弱读即可。

这两个条件和主正本路由须要满足的条件是有区别的:

  • 对于条件1,备正本路由只反对查问语句,不反对其余语句,这也是Paxos算法的实现要求。
  • 对于条件2,须要被动设置弱读标记ob_read_consistency=weak,能够通过hint、session等设置。

对于备正本路由,发往主正本和备正本都能够失常工作,因而备正本路由能够抉择变多了对于多个正本抉择的问题,请参考下文“路由策略”的内容)。在很多时候,你可能认为备正本路由只能发往备正本,而实际上发往主正本也能够失常工作

和备正本相干的一个重要话题就是读写拆散,申请进行读写拆散后,能够升高主正本压力,是一个很好用的性能。OBProxy 也实现了读写拆散性能,并在一直打磨细节,在 OceanBase 私有云等场景帮忙客户解决了性能问题。

3. 租户机器路由

有些时候咱们无奈获取主正本或备正本,此时就能够从租户机器中选取一台,这就是租户机器路由。
常见的租户机器路由场景如下:

  • SQL 自身不蕴含表名,如语句select 1语句。
  • 主正本或者备正本所在的机器有故障。
  • OBProxy 自身性能限度,如简单 SQL 无奈取得表名,无奈走正本路由。

通过租户机器路由,OBProxy 将 SQL 发往了租户所在的机器,因而能够保障性能失常。租户机器路由和备正本路由一样,有多个正本能够抉择,也存在着路由策略的问题。

4. 缓存信息

主正本路由、备正本路由和租户机器路由都须要通过sys租户查问正本路由信息,为了晋升性能和升高对sys租户的压力,OBProxy 对路由信息做了缓存。对于缓存信息,最重要的是时效性。你能够比照sys租户的缓存信息更新,sys租户的缓存信息能够通过定期拜访外部表创立和刷新,那么正本路由的缓存信息是否能够采纳此策略呢?

答案是:不能够。次要问题是拉取正本路由信息的SQL太多,会对sys租户造成很大的压力。SQL的数量 = 正本的个数 * OBProxy的数量。

OceanBase 数据库能够反对十万、百万分区,分区数极大。OBProxy 的数量受到部署架构影响,部署在利用端状态下,数量也很多。因而,缓存时效性对 OBProxy 是一大难题,应用了过期的缓存信息就会呈现大家常说的“路由不准”的问题。那么,怎么保障缓存的时效性呢?

咱们先看一下缓存信息在 OBProxy 中的内容。应用[email protected]账号登录,通过show proxyroute命令能够查看表的缓存信息,如下:

MySQL [(none)]> show proxyroute like 'ob1.hudson tt1 test sbtest1'\G*************************** 1. row ***************************        cluster_name: ob1.hudson         tenant_name: tt1       database_name: test          table_name: sbtest1               state: AVAIL       partition_num: 1         replica_num: 3            table_id: 1101710651081698     cluster_version: 2      schema_version: 1649196335597728         from_rslist: N         create_time: 2022-04-07 12:41:16     last_valid_time: 2022-04-07 12:41:16    last_access_time: 2022-04-07 12:41:16    last_update_time: 1970-01-01 08:00:00         expire_time: 2022-04-12 12:48:42relative_expire_time: 2022-04-07 12:40:41         server addr: server[0]=xx.xx.xx.xx:xx,leader,FULL; server[1]=xx.xx.xx.xx:xxfollower,FULL; server[2]=xx.xx.xx.xx:xx,follower,FULL;

这个例子展现了缓存蕴含的重要信息:集群名、租户名、库名、表名、分区数、正本数、工夫、地址信息和缓存状态等。其中,缓存状态是须要咱们重点关注的对象,缓存策略都是通过批改状态信息实现的,这些状态影响缓存的刷新机制。缓存信息分为如下5个状态。

  • BUILDING状态:缓存正在创立,需期待创立实现而后应用。
  • AVAIL状态:缓存失常,间接应用即可。
  • DIRTY状态:缓存生效,信息不精确。
  • UPDATING状态:生效的缓存正在更新过程中。
  • DELETED状态:缓存曾经备删除,不能够应用,后续会被清理掉。

咱们通过批改缓存状态就能刷新缓存状态,从而保障时效性,上面从创立、淘汰和刷新这三方面介绍缓存刷新机制。

  • 缓存创立:首次拜访分区时,OBProxy 通过查问 sys 租户 的 __all_virtual_proxy_schema取得,指定表名为实在表名,留神和租户路由信息局部辨别,创立好后缓存状态为AVAIL 。
  • 缓存淘汰:当 OBServer 返回路由不准时(ObServer 会通过OK报文中携带的is_partition_hit字段反馈),OBProxy 批改缓存状态为 DIRTY
  • 缓存刷新:当缓存信息变为DIRTY状态后,淘汰过期缓存,并从新创立或者更新缓存信息。

目前缓存淘汰次要通过 OBServer 的报文反馈实现,这样就无奈实时感知,只有呈现一次“谬误”路由后,能力刷新,这也是容易出问题一个中央。

5. 路由策略

5.1 路由策略介绍

路由策略用于从多正本中抉择出一个适合的正本。这里的多正本,可能来自备正本路由时抉择出的多个正本,也有可能是相似select 1 from dual这种语句,应用了租户的路由信息有多个正本(指租户的机器列表)。

路由策略次要有三种:LDC路由、Primary Zone路由和随机路由,路由策略优先级为 Primary Zone路由 > LDC路由 > 随机路由。

1)Primary Zone路由。在多正本抉择时,优先发往Primary Zone(租户的属性,Primary Zone指正本的Leader优先散布在Primary Zone中)所在的机器。为什么会有这种路由策略?首先,OceanBase 中罕用的高性能部署架构是租户的 Primary Zone 在一台机器,这样能够防止分布式系统的很多网络开销;其次,OBProxy 在主正本路由时,存在找不出表名和计算不出分区的状况,通过Primary Zone路由能够尽量发往主正本。

2)LDC路由是基于地址地位的路由,有两个重要的概念:IDC和Region。IDC示意逻辑机房概念,Region是城市的概念。OBProxy 和 OBServer 都能够设置LDC信息。通过LDC信息,OBProxy 能够确定和 OBServer 的地位关系。当咱们设置了LDC信息后,OBProxy就会默认应用LDC路由。

  • OBServer LDC设置。

OBserver 的每个Zone都能够设置Region属性/idc属性,Region通常代表城市的概念,通常设置为城市名(大小写敏感),IDC代表该Zone所处的机房信息,通常设置机房名(小写)。设置SQL如下:

alter system modify zone "z1" set region = "SHANGHAI";alter system modify zone "z1" set idc = "zue";

select * from __all_virtual_zone_stat;
图9 OBServer LDC设置

  • OBProxy设置LDC。

OBProxy通过配置项或者启动参数设置LDC信息。首先通过-i选项设置启动参数,而后执行配置项:执行SQL语句alter proxyconfig set proxy_idc_name='机房名';通过 OBProxy 执行外部命令show proxyinfo idc;能够查看proxy外部辨认的LDC部署状况。

图10 OBProxy设置LDC

你肯定在想有没有最佳实际,的确有。如果只有一个机房,LDC用途不大,因为咱们认为同机房机器间提早雷同。如果在同机房要应用LDC,须要划分出LDC架构,并设置 OBServer 和 OBProxy 的LDC属性。如果有多个机房,就能够依据机房和城市设置LDC,如私有云杭州可用区I、H机房,它们Region雷同,IDC名字不同。

某些非凡状况下,能够通过trick办法设置LDC影响LDC路由,但不太举荐你这么做。

3)随机路由。通过优先级路由后,如果还有多个正本,进行随机路由即可。如未开启Primary Zone路由或者未设置LDC路由,就会间接应用随机路由。

5.2 路由配置和查看

数据路由比较复杂的一个起因是有不同的路由策略,OBProxy默认策略是先进行主正本路由和备正本路由,没有正本则进行租户机器路由。如果只有一个正本被选中,则间接路由,否则依据策略路由。

对于 Primary Zone 路由和 LDC路由,受到配置项管制:

  • enable_primary_zone:为true示意应用 Primary Zone路由策略
  • proxy_idc_name:内容非空(内容为 idc 的名字)示意应用LDC路由

除了现有路由策略,有时咱们想应用其它路由策略,能够通过批改配置项proxy_route_policy管制实现,设置后新策略优先级最高。目前咱们常常设置的其它路由策略有两种,都和弱读无关。

  • "FOLLOWER_FIRST":优先发往备正本,如果无备正本可用发往主正本。
  • "FOLLOWER_ONLY":只能发往备正本,如果无备正本可用报错。

OBProxy 具体应用了什么路由策略,能够在 OBProxy 的日志中查看要害信息route_type,如ROUTE_TYPE_LEADER示意进行了主正本路由。ROUTE_TYPE_NONPARTITION_UNMERGE_LOCAL状况比较复杂,上面介绍次要关键字含意。

  • PARTITON:选取有正本数据的机器,不辨别正本的类型。
  • NONPARTITION:不关怀表数据分布,任何租户机器都能够。
  • FOLLOWER:发往备正本。
  • LEADER:发往主正本。
  • UNMERGE:发往不在合并状态的机器。
  • MERGE:发往在合并状态的机器。
  • LOCAL:发往同IDC(机房)机器。
  • REMOTE:发往同城不同IDC(机房)机器。
  • REGION:发往异地的机器。
  • READONLY:发往READONLY属性Zone内机器。
  • READWRITE:发往READWRITE属性Zone内机器。
  • DUP:复制表中,发往返制表所在的机器。

对于该问题,你也能够参考《高性能数据拜访中间件 OBProxy(三):问题排查和服务运维》。

6. 事务路由

下面介绍了单个SQL的路由策略,有些性能如事务性能蕴含一条或多条SQL。对于事务路由,事务的第一条语句受到上述策略影响,后续SQL不再进行路由,间接发往第一条语句发往的节点。

为什么事务路由只能发往第一条语句发往节点?你能够参考数据路由影响因素中的“性能因素”。目前咱们还未实现事务状态迁徙,所以有此限度。

7. 常见问题

置信当你看到限度要求后,感觉到此处可能有坑。上面我依照本文的叙述程序阐明常见的坑。

  • 无奈获取表名(主正本路由)

    • SQL太简单,目前 OBProxy 无奈辨认所有的SQL语句
    • SQL太长,OBProxy 存储SQL的buffer只有4K,SQL太长不会全解析
  • 分区计算失败(主正本路由)

    • OBProxy 无奈反对多分区键的计算如range(c1,c2);
    • SQL语句中没有分区键的表达式或者 OBProxy 未提出进去
    • 分区键表达式 OBProxy 无奈解决,如c1=now(),OBProxy还未反对now函数
  • 应用过期缓存(缓存信息)

    • OBProxy 无被动刷新机制
    • OBServer未进行路由反馈(如分布式打算 OBServer 不反馈)
  • 配置谬误:

    • 未设置路由策略为FOLLOWER_FIRST,弱读发往了主正本(备正本路由)
    • 未设置LDC路由信息或者信息设置谬误导致跨机房或者跨城(路由策略)

总结

数据路由性能点很多,并且性能点之间存在优先级问题,会让路由变得复杂。思考性能和高可用因素,OBProxy 路由性能还有一些能够欠缺的中央,如 加强 Parser 能力、反对多分区键路由计算、进步缓存实效性等。咱们也在一直进步路由能力,解决大家的问题。

课后互动

上期互动答案

问:JDBC和OBProxy建设了一个连贯,早晨OBProxy被kill掉重启,请问此时JDBC会抛出异样吗?

答:不会的。因为JDBC不感知 tcp 异样,所以只有在真正应用连贯时才会发现连贯呈现问题,这种问题就会比拟难排查。大家能够看JDBC的异样日志会打印上次发给 Server 的申请工夫和上次从Server接管到申请的工夫,如果接管申请的工夫大于发送申请工夫,那么上次申请应该是失常实现,可能就是上来就拿到了一个坏连贯。

本期互动问题

问:对于一个有表名的强读申请,如果SQL长度为7K,那么可能会如何路由?

欢送你在问答区发帖探讨,下篇文章揭晓答案。