
讲解 Paxos 协议之前,我们先普及下数据分区与分区副本的概念。当一个表很大的时候,可以水平拆分为若干个小的分区,存储在物理硬盘上。每个分区包含表的若干行记录。根据数据到分区的映射关系不同,可以分为 hash 分区、list 分区(按列表),range 分区(按范围)等。每一个分区,还可以用不同的维度再分为若干分区,叫做二级分区。为了数据安全,每个分区的数据在物理上存储多份,每一份叫做分区的一个副本。每个副本会存储在一个 Zone 中,且一个 Zone 只能有一个副本。我们用图上的例子解释下,一个交易记录表,按照用户 ID 可以分为 3 个 hash 分区就是红色、蓝色和黄色的三个分区,每一个 hash 分区再按照交易时间分为 4 个 range 分区,也就是这个表格一共被分割成了 3*4=12 个分区。为了更高的可靠性。每个分区又会有 3 个副本(假设这个集群有 3 个Zone),每个副本会分布到不同的 zone 内,避免单个zone 的故障影响业务。
副本的内容不单单只有硬盘上的静态数据,副本还包括内存的增量数据以及记录事务的日志。基于副本内容的不同,可以分为全能型副本、日志型副本、和只读型副本。全能型副本:有全量的数据,也参与投票,是最常用的副本。
日志型副本:只有日志,没有硬盘的静态数据和内存的增量数据,但它会参与投票,因它所消耗的物理资源(CPU、内存、硬盘)更少,它一般用于最后一个副本,可以有效降低成本。但由于它没有基线数据,所以它不会变成主副本为业务提供服务。
只读型副本:全量的数据,但它只做为一个 listener,实时追赶 Paxos 成员的日志,并在本地回放。这种副本可以在业务对读取数据的一致性要求不高的时候提供只读服务。因其不加入 Paxos 成员组,因此不会造成投票成员增加导致事务提交延时的增加。

可能大家有个疑问,数据库有很多表,每个表有很多分区,每个分区都有多个副本,这么多的副本,是如何打散到 Zone 内的服务器呢?会不会出现副本集中到 Zone 内的一台服务器这种情况呢。默认情况下,OceanBase 会自动的把这些副本打散到 Zone 内的机器中,避免让大多数副本集中到少数机器的情况,这样既可以均衡流量,也避免一台服务器故障后,影响太多的副本,造成大面积的重新选举。以上图为例,假设一个表有 8 个分区,每个分区有 3 个副本。我们可以看到,首先,每个Zone 都有 P1-P8 的副本,且只有一份。其次,我们看 Zone 内的两台服务器,每个服务器都有 4 个副本,是均匀分布的(前提是该表所在的租户的资源模块数量为 2)。所以 OceanBase集群会自动的将分区打散到 Zone 内的各个服务器中。另一个问题是,既然有主副本和从副本,需要管理员指定谁是主副本?谁是从副本么?答案是不需要。还是以上图为例,每个分区的 3 个副本,会有 1 个主副本和 2 个从副本,他们会组成一个Paxos 组。比如图中三个 P7 组成了一个 Paxos 组,自动选出谁是主,谁是从,比如图中 Zone3-Server2 的 P7 是主,将由它对外提供业务,应用要访问数据,默认只会访问主副本,而不会去找从副本。接下来我们对以上内容进行总结下:
1:OceanBase 的 Paxos 组是以分区为单位的,不是以表、数据库或者租户为单位的,是分区级的,在更细粒度上建立 Paxos 组,可以让数据管理更加灵活方便。
2:组成员会自动选举出主和从,不需要人工干预。
3:副本会均匀的分布在 Zone 内的机器上,避免单个 Zone 的故障影响业务。

会不会选出来的主副本都在同一个Zone 内,或者同一台服务器上,导致一台服务器负载太高,其他服务器只做备份,负载很低,造成忙闲不均的现象呢。答案是不会的,默认情况下,系统会自动的把主副本平均分配到 3 个 Zone 中,比如图中,Zone1有 3 个主,Zone2 有 2 个主,Zone3 有 3 个主,每个服务器也都是有主副本,有从副本。这样的好处是每个服务器都承载了部分业务的同时,也给其他服务器做着备份的工作,将业务负载均衡了。当然,在一些特殊场景下,管理员也可以通过 Primary zone 的功能将业务负载集中到某个 zone 内,以满足一些特定场景的需求,后面章节会重点介绍这一特性。如果应用要访问的数据不在一个服务器上,是否需要应用自己连接多个服务器,造成业务的复杂。答案是不需要的,比如应用 2,他要访问的数据分布在P6,P7,P8上,P6的主副本在Zone2-OB Server2,P7的主副本在Zone3-OB Server2, P8 的主副本在Zone1-OB Server2 中。应用是否需要了解这些细节呢?不需要,应用不需要知道他要访问的数据在哪里,他可以连接任何一台 OB Server,比如开始访问 P6 主副本所在的服务器(Zone2-OB Server2),该服务器会连接其他服务器获取数据后,返回给应用。这个过程对应用是透明的。通俗来讲,每台 OB Server 都是全功能的,都是相对独立的,都践行“首问责任制”和“最多访问一次”的概念,避免对业务的侵入性。
业务从 OceanBase 读取数据时候,只会从主副本读取数据。那么如果业务对数据库进行写操作,OceanBase 是如何确保数据持久化呢?Paxos 组成员通过 Redo-Log 的多数派强同步来确保数据持久化,以图中所示为例,
当应用要写数据到 P2 分区时,应用会连接到 P2 分区的主副本所在的机器(Zone2-OB Server1)中,
该服务器首先会将 Redo-Log 落盘,
并将 Redo-Log 同步请求发送到 P2 从副本所在的机器(Zone1-OB Server1 和 Zone3-OB Server1)中,
从副本完成日志落盘后,返回消息给主副本。
主副本只要收到其中一个从副本的回复后即可以返回应用(两个副本强同步已满足了多数派),而无需再等待其他从副本的反馈。
如果哪个从副本由于故障没有落盘成功怎么办?等故障恢复后,再从主副本那里追平数据就好。
这种机制有两个好处:
l 数据更新只要Redo-Log落盘就好,而Redo-Log的落盘是像记日记一样,是顺序写的,很快。实际更新的数据存储在内存,不用马上落盘.减少了数据落盘带来的寻址、写数据等时间,快速响应应用的写请求。
l Redo-Log 日志是同时同步给 2 个以上的副本,只要多数派落盘成功,不用等所有从副本落盘成功,可以快速响应应用。

虽然对于应用来说,如果它需要的数据位于多台 OB Server 上。它可以连接任何一台 OB Server,由其访问其他 OB Server 获取应用想要的所有数据。但万一这台服务器故障后,应用需要自身实现高可用的机制,增加了应用的复杂度。
OB Proxy 可以解决这个问题,OB Proxy 包含简单的 SQL Parser功能,可进行轻量的SQL 解析。
先从客户端发出的 SQL 语句中解析出数据库名和表名,
然后根据用户的租户名、数据库名、表名以及分区 ID 信息等信息查询路由表,获取分区的主/从副本所在 OB Server的 IP 地址信息。
有了这些信息之后,OB Proxy 将请求路由至主副本所在的机器上(Leader 位置无法确定时随机选择 OB Server),
同时将副本的位置信息更新到自己的 Location Cache 中,当再次访问时,会命中该 Cache 以加速访问。
当负载均衡被打破(如机器故障、集群扩容等),导致分区分布发生变化时,location cache 也会动态更新,应用无感知。另外,OB Proxy 不需要独立占用 1 台服务器,可以与 OB Server 共用一台服务器。单台OB Proxy 会带来单点故障,可以在前面前置 F5 负载均衡器或 DNS 域名服务器,由其来做负载均衡。但这也会带来延时增加的问题,如果应用对实时性要求高,也可以将 OB Proxy 部署到应用服务器中,没有了 F5 负载均衡器以及网络的延时,可以减少时延。当然,OBProxy 除了支持负载均衡功能外,也可以配置为其他功能,比如读写分离、备优先读、黑名单等功能,满足业务差异化的需求,这些知识将在后续课程中介绍。
有人可能会问,OBProxy 跟数据库中间件很像,都是在应用和数据库服务器之间。其实是完全不一样的架构。OB Proxy 是一个“无状态”的服务进程,不做数据持久化,不参与数据库引擎的计算任务。比如一台 OB Proxy 故障了,由于这台故障的服务器没有维护任何 session的状态,也没任何数据,业务可以重连其他 OB Proxy,重新开始 session。
负载均衡机制可以将主副本打散到不同 Zone 内的不同 Server 中,将业务流量分到不同服务器中,提升了系统整体的可用性和性能,但这也导致出现分布式事务的概率增大,带来更大的资源消耗。通过设置 Primary Zone,如上图配置 2,设置 Zone 优先级为Zone1>Zone2>Zone3,可以将主副本集中到 Zone1 中(如该表在 Zone1 有多个资源单元,主副本也会分布到该 Zone内的多台 Server 中),避免跨 Zone 访问,适合业务量不大,同时对时延敏感的在线处理业务。我们也可以配置为(Zone1=Zone2)>Zone3,这时主副本会均匀分布到 Zone1 和 Zone2中。这种配置比较适合三地五中心五副本方案,将业务汇聚到距离较近的城市(Zone1和Zone2),此时虽然存在分布式事务跨 Zone 执行,但由于 Zone1 和 Zone2 城市距离较近,总体时延是可控的。此时,距离较远的城市(Zone3)将没有主副本,所以它不承接业务,只承担从副本备份的角色。
OceanBase 的 Primary Zone 有不同的优先级,比如我们可以设置某租户的 Primary Zone的优先级为 Zone1=Zone2=Zone3,主副本被打散到集群中,实现负载均衡。但某个数据库/表,对时延很敏感,可以设置该数据库或者表的 Primary Zone 为 Zone1>Zone2>Zone3,将主副本集中到 Zone1 中,从而对主副本的分布实现更细粒度的管理。

