在分布式数据库系统领域,multi-master(多写点,Leader-less)是一个非常吸引人的特性,因为客户端可以随机请求任意一个节点。这种随机选择接入点(写入点)的特性,让系统高可用触手可及,因为当客户端发现某个节点出现故障时,可以直接更换另一个节点重试。只要系统没有完全宕机,重试几次就会成功,可以达到100%的高可用。传统的BasicPaxos常被误认为是Leader-less,即multi-master,但BasicPaxos只能用于确定一个实例的共识,需要结合日志复制状态机才能真正实现。如果复制组(多节点)没有指定Leader,那么就会出现争夺同一个位置的日志的情况,即在试图对位于的日志达成共识时会出现活锁这个位置。这种多个节点争抢同一个位置的情况会导致系统Unavailable,因为号称使用Paxos的多副本数据库系统通常还是需要显式指定Leader,并不是真正的Leader-less。借鉴vectorclocks和多复制组(比如MultiRaftGroup)的思想,可以避免多主冲突,因为不同的节点,作为各自组的Leader,分别写入不同的日志序列,并且有对于日志的同一位置根本没有冲突。以一个3节点的集群为例,通过预配置强制指定每个节点为主,结构为:有一个复制组G1,成员节点为{A,B,C},其中节点A为大师。同理,复制组G2和G3的master分别是B和C,当client请求不同的节点时,日志写入到不同复制组的日志序列中,所以不会有冲突。某一时刻,所有节点的状态为:该状态表示复制组G1共有6条日志,复制组G2为1,复制组G3有2条。至此,一切似乎都很顺利,写起来没有冲突,可以通过复制组形成多副本。然而现实不是这样的,一个只能写的数据库几乎没有用,数据库必须支持读,所以日志复制状态机架构必须要有状态机,也就是必须将三个日志序列应用到所有节点上每个节点的状态机实例。最简单的方法是在每个节点上应用3个日志序列合并(Merge)一个日志序列,通过一定的算法保证所有节点上的合并结果必须相同。比如按时间戳排序,从而保证状态机一致(same)。但是一个节点不能仅仅依靠自己本地的3个日志序列来合并。在每次merge的时候,需要获取其他replicationgroup的最新信息来判断自己本地的logsequence是否足够新?当然,我可以确定我是领导者的顺序,但其他两个不能。你需要去问其他节点,不能一直去问leader,因为你需要容忍某个replicationgroup情况的leader宕机。因此,对于其他的复制组,可以通过ReadIndex逻辑来判断是否是最新的。而当其他组的Leader宕机时,相应的日志序列会从其他Follower中完成。日志序列可能包含非幂等指令,通过添加时间戳,非幂等指令可以变为幂等。(需要文章演示)。以key为例,状态机中的key-value有初始化时间戳和最新更新时间戳{reset_time,modify_time}。当收到一个incr命令时,这个命令有一个时间戳,和key-value中的meta信息对比后,就可以知道Apply算法。ifkey.reset_time
