乐趣区

关于数据库:高性能数据访问中间件-OBProxy五一文讲透数据路由

上篇文章咱们介绍了 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:42
relative_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,那么可能会如何路由?

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

退出移动版