1

简介

本文将简单回顾传统数据库高可用的实现方式,并详细介绍巨杉分布式数据库的高可用实现。通过对本文的阅读,小伙伴们能够了解到传统数据库的高可用实现方式,包括;主备结构和集群架构;了解到大名鼎鼎的RAFT算法;然后最重要的,巨杉分布式数据库是如何实一致性的,如何保证在集群环境中实现数据不错不丢。

2

数据库高可用技术回顾

数据库系统存储了一个IT系统的业务数据,可以说是IT系统的大脑。数据库系统的可用性基本决定了IT系统的可用性,如果数据库系统宕机那么整个IT系统就停摆了,甚至如果数据库损坏还可能导致业务数据丢失,造成极大了经济损失。

传统的数据库系统,如:Oracle,DB2,SQL Server,MySQL等都是为单机环境设计的,通常的架构是一台服务器加上磁盘阵列的模式。在生产环境中,就算是采用了可靠性非常高的小型机及高端磁盘阵列,也难保数据库系统出现服务器宕机/掉电/网络故障/磁盘阵列故障等问题。就是说,数据库系统在运行的过程中出故障是肯定的。那怎么办呢?经过0101训练过的IT“攻城狮”们的大脑也是比较直的。一个网络会出故障,那么再加一个网络;一台服务器会出问题,那么再加一台服务器;一个磁盘阵列一份数据会出问题,那么再加一个磁盘阵列一份数据。“攻城狮”:“我们的口号是消除单点故障,保证数据库7*24可用”。PM:“又要加钱!”。

经过“攻城狮们”的实践,传统数据库的高可用分为3种架构:冷备架构/热备架构/集群架构。

2.1 冷备架构

冷备架构是主备架构的一种,通过增加冗余的网络和服务器,并增加集群管理软件,如:IBM HACMP,消除网络和服务器单点故障。如图所示:

主备服务器上都安装了数据库软件和集群管理软件,并且都能够通过SAN网络访问磁盘阵列。在正常情况下,主服务器启动数据库进程(备服务器上并不启动数据库进程),并访问存储在磁盘阵列中的数据库;集群管理软件在主备服务器上都启动,监控服务器/网络/IO/数据库进程状态并提供一个虚拟IP地址(在主服务器上)供应用访问。

如果集群管理软件发现主服务器不可用(不可用的情况比较多,如掉电/网络断/内存CPU故障/无法访问磁盘阵列/数据库进程宕机等等),就会启动切换流程。备服务器:“嘿嘿,终于轮到我上场了,脚都蹲麻了。”

卸载原来挂载在主服务器上的磁盘阵列设备及文件系统。

对数据库文件系统进行检测后挂载到备服务器上。

取消主服务器上配置的虚拟IP,把虚拟IP配置到备服务器上。

启动备服务器上的数据库进程;数据库进程检查数据库日志,对事务进行重新提交或者回滚。

备服务器数据库正常后开始提供服务,切换完成。

优缺点

优点:架构和配置简单,相对成本较低。

缺点:需要重新挂载文件系统,启动数据库进程,数据库日志检查,切换时间以分钟计,如果需要回滚的事务太多,可能是几十分钟,对可用性要求比较高的业务无法忍受;备服务器冷备浪费一台服务器资源;磁盘阵列只有一个是单点;数据只有一份是单点。

2.2 热备架构

热备架构是主备架构的另外一种,在冷备架构中加入了另外一台磁盘阵列存储多一份的数据库数据,并且备服务器的数据库进程是启动的持续的对主服务器发送的日志进行重用。热备架构的实现包括:IBM DB2 HADR,Oracle DataGurd,MySQL Binglog Replication等。

如图所示:

正常情况下,业务应用通过虚拟IP访问主服务器,主服务器访问自己磁盘阵列中的主数据库;主服务器数据库进程启动日志复制功能,把事务日志复制到备服务器的数据库进程;备服务器的数据库进程把日志重用到备数据库中,完成数据同步。

如果主服务器不可用,集群管理软件会启动切换流程。

集群管理软件取消主服务器的虚拟IP配置,并把虚拟IP配置到备服务器。 被服务器的数据库进程执行切换操作,从备升级到主。 备服务器的数据库进程完成日志重用后,可用开始提供服务。

优点:由于备服务器的数据库进程是启动状态,并且事务日志持续重用,切换时间短(秒级别);数据是两份,排除了磁盘阵列和数据的单点;备服务器一般不能写但是可以提供读服务。缺点:增加了一套磁盘阵列,增加了成本。

2.3 集群架构

前面两种架构解决了高可用问题,但是数据库系统都只能进行纵向扩展(增加单个服务器的CPU,内存),不能进行横向扩展(增加服务器)的方式实现性能扩充。

客户:”我要横向扩展“。

攻城狮:”我好难啊,但是可以有“。

通过在数据库系统中提供共享存储的能力,如:IBM DB2 pureScale,Oracle RAC,多台服务器上的数据库进程能够并行访问一个共享存储上的数据库数据。

如图所示:

正常情况下,业务应用可以连接到任意一台数据库服务器上执行数据库读写操作;数据库进程通过并行访问控制实现对共享磁盘阵列中的数据库的并行读写。

如果一台服务器不可用,那么连接到这台服务器上的应用会自动重新连接到另外的数据库服务器上,实现高可用性。

优点:

切换时间短(秒级);可以实现横向扩展;具有负载均衡能力;

缺点:

横向扩展能力有限,一般2台服务器的案例居多,3台服务器以上的案例很少,服务器太多性能会不增反降;成本较高;配置管理复杂,DBA需要非常高的数据库管理能力。

3

巨杉分布式数据库的高可用技术

小伙伴们可能要问:”传统数据库已经实现了高可用和横向扩展,为啥要用分布式数据库呢?“

攻城狮:”答。首先,传统数据库横向扩展能力有限一般2-3台服务器,无法应对现在的大数据场景;其次,贵啊。花几百万用2台小型机加高端磁盘阵列加ORACLE RAC搭建一个核心数据库,为了稳定性也就罢了,但是需要处理几百TB数据,需要几十上百台服务器的数据库系统怎么搞?“

巨杉数据库:”该我登场了。不需要磁盘整理,不需要SAN网络,使用PC服务器加内置磁盘加分布式数据库软件,实现低成本/高可用性/高可扩展性/高性能的数据库系统。欧耶!“

由于采用了PC服务器加内置磁盘的方式,数据一致性和高可用是分布式数据库设计里面最重要的一环。这里我们将讨论巨杉数据库的高可用实现技术。巨杉数据库的高可用技术原理与RAFT算法类似,借鉴了RAFT选举算法进行了优化,以便能够更好的支持分布式数据库的场景。

3.1 Raft算法

RAFT是一种为了管理复制日志的一致性的算法。那么RAFT算法是怎么来的了。在RAFT算法出现之前,Paxos算法统治着一致性算法邻域,但是Paxos的显著缺点是”十分难于理解“,当然也就难于实现。2013年,斯坦福的Diego Ongaro和John Ousterhout以易于理解为目标设计了RAFT一致性算法,并发表论文《In Search of an Understandable Consensus Algorithm》。RAFT算法的易于理解和易于实现使得其在业界得到了广泛的应用,并在这些实践中证明了算法的正确性。给大佬们点赞,大佬们的想法是”这个世界给不了我想要的,那我就改变这个世界“。

这里将简单介绍一下RAFT算法的原理,以帮助大家理解巨杉分布式数据库的高可用性。如果想详细的了解RAFT算法可以参考github上的文章:

https://github.com/maemual/raft-zh_cn/blob/master/raft-zh_cn.md

RAFT算法的设计思路就是复杂问题简单化,把一组服务器的数据一致性问题分解为3个小问题。这3小问题包括:

领导选举:当现存的领导人宕机的时候,一个新的领导人需要被选举出来 日志复制:领导人必须从客户端接收日志然后复制到集群中的其他节点,并且强制要求其他节点的日志保持和自己相同。 安全性:在 Raft 中安全性的关键是状态机安全:如果有任何的服务器节点已经应用了一个确定的日志条目到它的状态机中,那么其他服务器节点不能在同一个日志索引位置应用一个不同的指令。

通过解决上面的3个小问题来,Raft解决了一致性这个大问题。 Raft算法还简化了复制状态机的数量,每台服务器一定会处于三种状态:领导人(Leader),候选人(Candidate),追随者(Follower)。状态的转换如图所示:

追随者只响应其他服务器的请求。如果追随者没有收到任何消息,它会成为一个候选人并且开始一次选举。收到大多数服务器投票的候选人会成为新的领导人。领导人在它们宕机之前会一直保持领导人的状态。

3.1.1 领导者选举

RAFT算法建议一组服务器至少包括5台服务器,这样在有2台服务器宕机的情况下,集群任然能够提供服务。服务器的状态只能是3中状态中的一种,并且在一个集群里只能有一个领导人。集群服务器之间通过心跳信息相互了解状态情况并触发选举。当服务器程序启动时,他们都是跟随者身份。一个服务器节点继续保持着跟随者状态只要他从领导人或者候选者处接收到有效的 RPCs。领导者周期性的向所有跟随者发送心跳包(即不包含日志项内容的附加日志项 RPCs)来维持自己的权威。如果一个跟随者在一段时间里没有接收到任何消息,也就是选举超时,那么他就会认为系统中没有可用的领导者,并且发起选举以选出新的领导者。

要开始一次选举过程,跟随者先要增加自己的当前任期号并且转换到候选人状态。然后他会并行的向集群中的其他服务器节点发送请求投票的 RPCs 来给自己投票。候选人会继续保持着当前状态直到以下三件事情之一发生:(a) 他自己赢得了这次的选举,(b) 其他的服务器成为领导者,(c) 一段时间之后没有任何一个获胜的人。候选人必须获得集群中的一半服务器以上的投票才能成为领导人。

3.1.2 日志复制

一旦一个领导人被选举出来,他就开始为客户端提供服务。客户端的每一个请求都包含一条被复制状态机执行的指令。领导人把这条指令作为一条新的日志条目附加到日志中去,然后并行的发起附加条目 RPCs 给其他的服务器,让他们复制这条日志条目。当这条日志条目被安全的复制(下面会介绍),领导人会应用这条日志条目到它的状态机中然后把执行的结果返回给客户端。如果跟随者崩溃或者运行缓慢,再或者网络丢包,领导人会不断的重复尝试附加日志条目 RPCs (尽管已经回复了客户端)直到所有的跟随者都最终存储了所有的日志条目。

领导人来决定什么时候把日志条目应用到状态机中是安全的;这种日志条目被称为已提交。Raft 算法保证所有已提交的日志条目都是持久化的并且最终会被所有可用的状态机执行。在领导人将创建的日志条目复制到大多数的服务器上的时候,日志条目就会被提交。同时,领导人的日志中之前的所有日志条目也都会被提交,包括由其他领导人创建的条目。领导人跟踪了最大的将会被提交的日志项的索引,并且索引值会被包含在未来的所有附加日志 RPCs (包括心跳包),这样其他的服务器才能最终知道领导人的提交位置。一旦跟随者知道一条日志条目已经被提交,那么他也会将这个日志条目应用到本地的状态机中(按照日志的顺序)。

日志复制机制展示出了一致性特性:Raft 能够接受,复制并应用新的日志条目只要大部分的机器是工作的;在通常的情况下,新的日志条目可以在一次 RPC 中被复制给集群中的大多数机器;并且单个的缓慢的跟随者不会影响整体的性能。

3.1.3 安全性

然而,到目前为止描述的机制并不能充分的保证每一个状态机会按照相同的顺序执行相同的指令。例如,一个跟随者可能会进入不可用状态同时领导人已经提交了若干的日志条目,然后这个跟随者可能会被选举为领导人并且覆盖这些日志条目;因此,不同的状态机可能会执行不同的指令序列。

这一节通过在领导选举的时候增加一些限制来完善 Raft 算法。这一限制保证了任何的领导人对于给定的任期号,都拥有了之前任期的所有被提交的日志条目。增加这一选举时的限制,我们对于提交时的规则也更加清晰。最终,我们将展示对于领导人完整特性的简要证明,并且说明领导人完整性特性是如何引导复制状态机做出正确行为的。

选举安全性

Raft 使用了一种更加简单的方法,它可以保证所有之前的任期号中已经提交的日志条目在选举的时候都会出现在新的领导人中,不需要传送这些日志条目给领导人。这意味着日志条目的传送是单向的,只从领导人传给跟随者,并且领导人从不会覆盖自身本地日志中已经存在的条目。

Raft 使用投票的方式来阻止一个候选人赢得选举除非这个候选人包含了所有已经提交的日志条目。候选人为了赢得选举必须联系集群中的大部分节点,这意味着每一个已经提交的日志条目在这些服务器节点中肯定存在于至少一个节点上。如果候选人的日志至少和大多数的服务器节点一样新(这个新的定义会在下面讨论),那么他一定持有了所有已经提交的日志条目。请求投票 RPC 实现了这样的限制:RPC 中包含了候选人的日志信息,然后投票人会拒绝掉那些日志没有自己新的投票请求。

Raft 通过比较两份日志中最后一条日志条目的索引值和任期号定义谁的日志比较新。如果两份日志最后的条目的任期号不同,那么任期号大的日志更加新。如果两份日志最后的条目任期号相同,那么日志比较长的那个就更加新。

提交之前任期内的日志条目

RAFT中领导人知道一条当前任期内的日志记录是可以被提交的,只要它被存储到了大多数的服务器上。如果一个领导人在提交日志条目之前崩溃了,未来后续的领导人会继续尝试复制这条日志记录。RAFT的领导人选举机制保证了新领导人的日志中必然包含了前一个任期的已经复制到大多数服务器的最新的任期号和最新的日志条目,这样新领导人就可以通过日志匹配特性,把之前任期的日志条目复制到其它服务器上,实现间接提交。

3.2 巨杉数据库的高可用实现

由于分布式数据库必须具有数据库的ACID(原子性,一致性,隔离性,持久性)及分布式事务能力,并且还需要提供高性能的并行计算能力,其场景远比RAFT算法中提到的复杂的多。

例如:RAFT算法通过保证日志的顺序来保证各个节点数据的一致性,但是在数据库的事务并行执行过程中,每个事务都可能产生多个日志记录,多个事务的日志记录很可能是交叉产生的而不是顺序的,要在分布式环境中保持事务的原子性,就要求一个事务产生的所有日志记录是可追踪的,在每个节点上这些日志都必须存在,这样在事务提交或者回滚的时候才能够在每个节点上保持原子性。在事务执行过程中,有可能会更新大量数据(批处理事务),这些更新操作在分布式数据库中不可能采用等到主节点执行完成,然后再复制到从节点的方法,这种方法的执行性能太差,无法满足业务需求。巨杉数据库的做法是事务中的每个写操作都会产生一个日志记录,主节点会把这些日志记录复制到从节点重做,并不会等到事务提交才开始复制重做,然后在事务提交的时候,在复制组内部实现二阶段提交算法,保证事务在复制组中的原子性和一致性。

另外,RAFT算法中的命令提交成功条件是:集群中的大部分服务器持有了这条命令的日志记录。RAFT算法能保证集群是最终一致的,但不保证某个时刻集群是一致的。这种机制在实现集群灾备的时候会有问题。例如:在5台服务器的集群中,3台在中心A,2台在中心B,如果领导人在中心A中,那么A中的3台服务器处于同一个网络,通常情况下日志复制时间都要比处于远端中心B的2台服务器短,也就是说不能保证中心B的服务器中必定持有最新的日志记录,如果中心A出现灾难事故,无法恢复,那么中心B中的服务器的数据是不完整的。这中情况对于某些业务(银行账务)是无法接受的。巨杉数据库的做法是设置写副本数参数,强制要求必须有多少个节点都持有最新日志记录,才算是复制成功。

所以,巨杉分布式数据库在节点选举的实现中优化了RAFT算法,而在数据同步/日志复制则完全开发了自己的算法。在巨杉分布式数据库的架构中,主节点对应了RAFT算法中的领导人,从节点对应跟随人,候选人是一样的;巨杉数据库的复制日志对应了RAFT算法中的日志记录;巨杉数据库的复制组对应了一个RAFT算法中的一组服务器。巨杉数据库在一个集群里面支持多个复制组,每个复制组都可以看作是一组RAFT算法的服务器。

巨杉分布式数据库的节点类型分为:协调节点,编目节点,数据节点。其中,多个协调节点的任务是接收命令,并分发命令到编目节点和数据节点,本身不保存数据,相互之间也没有关系,所以不需要考虑可用性问题。编目节点其实就是一种特殊的数据节点,其高可用性是和数据节点一样的。协调节点可以看作是数据节点复制组的客户端。

巨杉分布式数据库也是通过解决3个小问题,从而实现了集群数据一致性这个大问题的。

3.2.1 节点选举

巨杉数据库的复制组可以包含1个到7个数据节点,但是如果要提供高可用特性复制组的节点数量必须大于等于3。每个复制组在同一时间只会有一个主节点,其它的都是从节点,如果处于选主阶段,每个从节点都可以是候选节点。巨杉数据库选主也是采用同组节点投票的方式,获得半数以上选票的节点成为主节点。

为了保证选主成功并且选出的主节点包含了最新的数据库日志,巨杉数据库在RAFT算法的基础上做了一些优化,特别是在候选节点的资格选择上。

从节点要成为候选节点并发起选举请求必须具有的条件是:自己不是主节点,剩下能与自己心跳通讯的节点数量必须大于半数以上,自己的LSN(日志序列号)比其它节点的LSN新。复制组在主节点存在的情况下不能自动发起选举请求(可以采用手工发起的方式切换主节点),只有当主节点不可用的情况下,从节点才可以发起选主请求。如果是集群分裂的情况,比如5个节点由于网络原因分裂为相互不通的2节点集群和3节点集群,如果当前主节点在3节点的集群中,那么由于此集群的节点存活数大于半数,不会发生选主,如果当前主节点在2节点的集群中,那么主节点将自动降为从节点,并且3节点的集群将发起选主请求,重新选择一个主节点。

在复制组所有节点正常的时候,每个节点都会通过共享心跳信息sharing-beat共享状态信息。共享心跳信息包括:心跳 ID、自身开始LSN、自身终止LSN、时间戳、数据组版本号、自身当前的角色和同步状态。如图所示:

每个节点都维护一张 status-sharing table 表,用来记录节点状态。sharing-beat 每2秒发送一次,采集应答信息,若连续两次未收到应答信息,则认为节点宕机。节点进程中的ReplReader(复制监听线程)负责节点状态信息的接收和发送。

从节点发起选主之前,会检查共享心跳信息中本节点的LSN以及其它从节点的LSN,如果自己LSN大于等于其它从节点的LSN,就可以发起选主请求,否则就不发起。

在选主中,如果多个候选从节点的LSN相同,并且都发起了请求,那么将比较各从节点的权重配置参数(weight)。复制组的每个节点都可单独配置不同的权重,从0到100,数字越大权重越高,相同LSN的从节点,权重高的选主成功。

如果多个从节点的LSN相同,权重配置也相同,那么将比较这些从节点的节点号,在创建节点的时候,每个节点的节点号是不同的,节点号大的将选主成功。

从上面可以看出巨杉分布式数据库在选主中,优化了选主流程,在RAFT算法的基础上增加了权重和节点号的判断,这样就不会出现RAFT算法中多个节点选主票数相同导致失败的情况。

下面举一个简单例子,描述一下巨杉数据库的选主流程。

如图所示,一个3节点的复制组,包括:主节点A,从节点B,从节点C。在正常情况下,三个节点通过心跳共享状态信息。如果主节点A由于某种原因故障(服务器掉电/网络故障/磁盘故障等等)将自动降为从节点,集群开始重新选主。

两个从节点,发现2轮没有收到主节点的心跳信息,则认为主节点宕机。

从节点B:”嘿嘿,主节点挂了,我有机会当老大了。我看看,我是从节点,LSN是最新的。嗨,C兄,我的LSN是XXX,我要当老大,你同意吗?“

从节点C:”嘎嘎,老大挂了!不当大哥很多年,有点想念。我看看,我是从节点,LSN是最新的,有机会。嗨,B兄,我的LSN是XXX,我要当老大,你同意吗?“

从节点B:”C兄也想当老大,LSN和我一样。嗨C兄,我的LSN也是XXX,我的权重是80,还是我当?”

从节点C:“B兄不好意思,我的权重也是80,我也想当。我的节点号是1008。”

从节点B:“C兄的节点号是1008,我的是1006;输了输了,C兄你是老大了。”

从节点C:“我自己投了自己一票,B兄投了我一篇,现在是2票,大于3/2+1,嗯够了。嗨B兄我是老大了。”

从节点B:“收到,老大。”

重新选主成功后,新主节点会通知编目节点变更主节点信息;协调节点会从编目节点同步节点状态信息,并连接到新主节点;协调节点也可以通过遍历节点的方式来获得新主节点信息。这样就在主节点宕机后实现了高可用能力。原主节点A恢复后,自动成为从节点。

3.2.2 日志复制

巨杉数据库的日志复制机制与RAFT的日志复制机制不同。首先,RAFT算法的日志总是从领导人(Leader)发送给追随者(Follower),追随者之间不会交换日志数据;而巨杉数据库的日志复制在源节点(发送日志的节点)和目标节点(请求日志的节点)之间进行,并且是目标节点主动向源节点请求日志数据,源节点大多数情况是主节点,但其它从节点也可以是源节点,目标节点只能是从节点。其次,RAFT算法中如果领导人没有追随者的日志复制返回信息,领导人会持续的给这个追随者发送日志直到收到返回信息,都是增量复制;而巨杉数据库的数据同步复制可以是日志增量复制,也可能触发数据全量同步。

巨杉数据库的日志复制不采用RAFT算法是因为其在分布式数据库的场景中并不适合。例如,如果某个从节点宕机,而按照RAFT算法主节点持续的向此从节点发送日志信息,而日志数据又很大,那么就会占用主节点大量的CPU/内存/网络资源,严重的影响主节点的性能,有可能造成阻塞;巨杉数据库采用从节点请求的方式,只有从节点是正常状态的时候才会请求新的日志数据,并清楚的给出需要哪些日志数据,不会造成重复发送问题,并且从节点可以作为源节点以可以大大减少主节点的工作负载。另外,由于实际环境中,存储空间是有限的,数据库能够配置的日志空间也是有限的,所以巨杉数据库的数据同步模式分为日志增量同步和数据全量同步两种方式。如果目标节点需要同步的数据都包含在源节点的复制日志空间中,就进行日志增量同步;如果数据已经不在复制日志空间中,那么需要进行全量同步。最重要的是,分布式数据库要求支持ACID及事务的能力,其场景比RAFT算法描述的日志复制场景更复杂,巨杉数据库采用两阶段提交的算法在兼顾性能和事务一致性的情况下支持复制组内部的事务能力。

日志增量同步模式

在数据节点和编目节点中,任何数据增删改操作均会写入日志。SequoiaDB 会首先将日志写入日志缓冲区,然后将其异步写入本地磁盘。

每个数据复制会在两个节点间进行:

源节点: 为包含新数据的节点。主节点并不一定永远是复制的源节点。

目标节点: 为请求进行数据复制的节点。

复制过程中,目标节点选择一个与其最接近的节点(在共享的节点状态表中,包含了每个节点的开始LSN和结束LSN可用计算哪个与自己最接近),然后向其发送一个复制请求。源节点接到复制请求后,会将目标节点请求的同步点之后的日志记录打包并发送给目标节点,目标节点接收到数据包后会重新处理日志中的所有操作。

节点之间的复制有两个状态:

对等状态(PEER):当目标节点请求的日志依然存在于源节点的日志缓冲区中,两节点之间为对等状态

远程追赶状态(RemoteCatchup):当目标节点请求的日志不存在于源节点的日志缓冲区中,但依然存在于源节点的日志文件中,两节点之间为远程追赶状态

如果目标节点请求的日志已经不再存在于源节点的日志文件中,目标节点则进入全量同步状态。

当两节点处于对等状态时,同步请求在源节点可以直接从内存中获取数据,因此目标节点选择复制源节点时,总会尝试选择距离自己当前日志点最近的数据节点,使其所包含的日志尽量坐落在内存中。

数据全量同步

在分区组内,当一个新的节点加入分区组,或者故障节点重新加入分区组(故障节点日志版本与其它节点相差过大,超过了节点事务日志空间的大小;或者重启发现故障节点的数据不一致),需要进行数据全量同步,以保障新的节点与现有节点之间数据的一致性。

在进行数据全量同步时有两个节点参与:

源节点: 为包含有效数据的节点。主节点并不一定永远是同步的源节点。任何与主节点处于同步状态的从节点均可作为源节点进行数据同步。

目标节点: 为新加入组,或重新入组的故障节点。同步时该节点下原有的数据会被废弃。

全量同步发生时,目标节点会定期向源节点请求数据。源节点将数据打包后作为大数据块发送给目标节点。当目标节点重做该数据块内所有数据后,向源节点请求新的数据块。

为保障源节点在同步时可进行写操作,所有已经被发送给目标节点的数据页如果被更改,其更新会被同步到目标节点,以保障全量同步过程中更新的数据不会损失。

同步副本数

巨杉数据库在实现数据同步的时候,为了适应不同的业务场景,可以为每个集合单独设置ReplSize(写操作同步副本数)参数。

ReplSize默认值为1。其可选取值如下:

-1:表示写请求需同步到该复制组若干活跃的节点之后,数据库写操作才返回应答给客户端。 0:表示写请求需同步到该复制组的所有节点之后,数据库写操作才返回应答给客户端。 1 - 7:表示写请求需同步到该复制组指定数量个节点之后,数据库写操作才返回应答给客户端。

在实际项目中,为了保证数据不丢失,需要把ReplSize设置为大于1,或者-1。例如,如果一个集合的ReplSize设置为2,那么主节点写操作必须等到有一个从节点返回成功才会给协调节点返回成功,这样能够保证如果此时主节点宕机,至少有一个从节点已经持有了成功的日志数据,在重新选主时,这个从节点根据LSN最新规则,必然成为候选者,从而保证数据不丢失。

3.3 安全性

巨杉数据库的集群一致性算法为了保证数据不错不丢,保证数据的安全性,也对选举规则和复制规则做了一些限制条件。

选举限制

前面也提到,在巨杉数据库中节点想要成为候选节点并发起选主请求,必须具备的条件是:自己不是主节点,剩下能与自己心跳通讯的节点数量必须大于半数以上,自己的LSN(日志序列号)比其它节点的LSN新。这些规则保证了,新主节点的数据是最新的,而且能够与大于半数以上的其它节点通讯。

日志复制限制

巨杉数据库在复制组内部采用两阶段提交的算法支持事务能力,并使用表的配置参数ReplSize(写副本数)强制主节点必须同步成功多少个从节点。通过这些手段,巨杉数据库可以保证复制组内部节点的数据和事务的一致性/完整性。

在日志复制方面,复制组的主节点将保证每一个日志(包含一个唯一并递增的LSN)都能够按照日志复制的规则复制到从节点上。例如,如果一张表的ReplSize参数设置为2,主节点保证至少在收到一个从节点的日志复制完成的确认后才会返回成功给协调节点,否则返回失败;如果ReplSize设置为-1,主节点将在收到所有活动从节点的日志复制成功的确认后才会返回成功给协调节点,否则返回失败。

在事务完整性方面,巨杉数据库在复制组内的两阶段提交事务实现流程如下:

客户端使用transBegin()开始事务,并发送给协调节点,协调节点再发送给主节点执行,主节点将生成一个唯一的事务ID。 在主节点接收到第一个写操作后(如insert,update,delete),会产生一条日志记录(日志记录中包含了事务ID)。这条日志记录将根据规则(复制副本数,即使是ReplSize设置为1,巨杉数据库在执行事务的时候都必须保证有一个从节点持有了事务ID的日志记录)发送给其它从节点并确认成功。 主节点将继续完成其它操作,任何写操作都将产生一条新的日志记录,并根据复制规则发送给其它从节点并确认成功。 主节点收到transCommit()指令后,将开始二阶段提交。 主节点首先执行第一阶段,预提交(pre-commit)。在此阶段将产生一条预提交日志记录,并发送给从节点并确保从节点确认收到。 然后主节点执行第二阶段,真正提交(Snd-Commit)。在此阶段会产生一条提交日志记录,并发送给从节点执行并确保从节点成功收到。当这个阶段成功后,整个事务才确认成功,并返回给协调节点成功。

在整个事务过程中,如果有任何违反复制同步规则的情况发生,导致事务无法进行下去(例如:在执行插入数据的时候,数据所在的表的ReplSize是3,而由于某种原因复制组包括主节点在内只有2给活动节点,那么插入操作将失败,导数事务无法继续进行),那么将进行事务回滚,把之前已经执行的操作复原。 在事务过程中如果出现主节点宕机等情况,巨杉数据库将根据事务执行的情况在新的主节点选举成功后自动判断是继续提交事务还是回滚事务,解决可疑事务问题。

4

总结

传统数据库系统在过去很好的支撑了整个IT系统的运行,并在”攻城狮们“的努力下,通过主备和集群架构实现了7*24的连续业务运行,通过集群共享架构实现了横向扩展能力。但是,传统数据库由于架构原因,无法满足目前大数据/云环境/微服务对数据库高并发/高扩展性的要求。

分布式数据库作为后来者,在架构设计时就考虑了以低成本硬件提供高并发/高扩展/高可用/高性能的能力。巨杉分布式数据库通过借鉴业界领先的高可用算法,并结合分布式数据库场景的特点优化了这些算法,特别是在集群数据一致性中,完全自主研发并实现了这些算法,在保证ACID及事务功能的前提下,实现了高并发/高可用/高扩展/高性能能力。可以说,巨杉分布式数据库的高可用及灾备能力在目前已有的分布式数据库系统中都是处于领先的地位。