[TOC] # 两阶段提交协议(2PC) 保证多个节点操作的原子性,实现事务操作。 包含两类节点:协调者(coordinator)和参与者(participants),协调者只有一个,参与者可以有多个。 **Phase1:**请求阶段,协调者通知事务参与者准备提交或者取消事务,然后进入表决过程。在表决过程中,参与者告知协调者自己的决策:如果事务在本地执行成功,就告知同意,否者告知取消。 **Phase2:**提交阶段,协调者将基于表决阶段的投票结果进行决策,当且仅当所有参与者同意提交事务,决策结果才为同意,否者为取消。协调者把决策结果通知所有的参与者,参与者接收到协调者发来的消息后执行相应的操作。 # Paxos协议 Paxos用于确保多个节点对某个投票达成一致。常用来选举主节点,当主节点出现故障时,使用Paxos协议就可以从备节点中选举出新的主节点。也用来构建高可用的全局服务,例如分布式锁服务,全局命名和配置服务等,Apache Zookeeper就实现了Paxos。 Paxos协议涉及两类节点:提议者(proposer)和接受者(acceptor)。 在只有一个proposer的情况下,Paxos协议执行步骤如下: 1. 批准:proposer发送提议给acceptor,acceptor决定接受或者拒绝这个提议; 2. 确认:如果超过一半的acceptor接受,则提议生效,proposer发送acknowledge消息通知所有的acceptor提议生效。 如果存在网络分区的情况下,可能会存在多个proposer,用提议号来控制每个提议,只有提议号最大的才会被接受。 ![](index_files/f257d633-48e9-431e-9206-625ec36bfab5.jpg) # Raft协议 Raft和Poxas同为一致性协议,但是更容易理解,也更容易实现。 [Raft可视化](http://thesecretlivesofdata.com/raft/) 有三种节点:Follower、Candidate和Leader。 Leader会周期性的发送消息给Follower。每个Follower都设置了一个随机的竞选超时时间,一般为150ms~300ms,如果在这个时间内没有收到Leader的消息,则节点就会变成Candidate,进入竞选阶段。最开始系统只有Follower。 ![](index_files/5.gif) Candidate会请求其它所有节点的投票,如果一个Candidate获得多数票,则它成为Leader。 所有来自客户端的修改都会被传到Leader,每个修改被作为一个entry添加到节点的日志。Leader先不会修改其节点值,而是把entry都赋值到所有的Follower,只有所有的Follower都写入这个entry,Leader才会修改节点值,并且通知所有的Follower也修改节点值。 ![](index_files/4.gif) 该协议也可以用来处理网络分区的问题。 ![](index_files/3.gif) # 拜占庭将军问题 该问题主要用于保证分布式系统的一致性和可用性。 ## 问题场景 拜占庭帝国想要进攻一个强大的敌人,为此派出了10支军队去包围这个敌人。这个敌人虽不比拜占庭帝国,但也足以抵御5支常规拜占庭军队的同时袭击。基于一些原因,这10支军队不能集合在一起单点突破,必须在分开的包围状态下同时攻击。他们任一支军队单独进攻都毫无胜算,除非有至少6支军队同时袭击才能攻下敌国。他们分散在敌国的四周,依靠通信兵相互通信来协商进攻意向及进攻时间。困扰这些将军的问题是,他们不确定他们中是否有叛徒,叛徒可能擅自变更进攻意向或者进攻时间。在这种状态下,拜占庭将军们能否找到一种分布式的协议来让他们能够远程协商,从而赢取战斗?这就是著名的拜占庭将军问题。 ## 相关问题:两军问题 拜占庭将军问题中并不去考虑通信兵是否会被截获或无法传达信息等问题,即消息传递的信道绝无问题。如果需要考虑信道是有问题的,这涉及到了另一个相关问题:两军问题。 ![](index_files/a8ec729f-cb9b-4daa-93a4-c6851db6e111.jpg) 白军驻扎在沟渠里,蓝军则分散在沟渠两边。白军比任何一支蓝军都更为强大,但是蓝军若能同时合力进攻则能够打败白军。他们不能够远程的沟通,只能派遣通信兵穿过沟渠去通知对方蓝军协商进攻时间。是否存在一个能使蓝军必胜的通信协议,这就是两军问题。通信兵得经过敌人的沟渠,在这过程中他可能被捕,也就是说,两军问题中信道是不可靠的,并且其中没有叛徒之说,这就是两军问题和拜占庭将军问题的根本性不同。 倘若1号蓝军(简称1)向2号蓝军(简称2)派出了通信兵,若1要知道2是否收到了自己的信息,1必须要求2给自己传输一个回执,说“你的信息我已经收到了,我同意你提议的明天早上10点9分准时进攻”。然而,就算2已经送出了这条信息,2也不能确定1就一定会在这个时间进攻,因为2发出的回执1并不一定能够收到。所以,1必须再给2发出一个回执说“我收到了”,但是1也不会知道2是否收到了这样一个回执,所以1还会期待一个2的回执。但在这个系统中永远需要存在一个回执,这对于两方来说都并不一定能够达成十足的确信。更要命的是,我们还没有考虑,通信兵的信息还有可能被篡改。由此可见,经典情形下两军问题是不可解的,并不存在一个能使蓝军一定胜利的通信协议。 不幸的是,两军问题作为现代通信系统中必须解决的问题,我们尚不能将之完全解决,这意味着你我传输信息时仍然可能出现丢失、监听或篡改的情况。但我们能不能通过一种相对可靠的方式来解决大部分情形呢?这需要谈到TCP协议。 ![](index_files/10a84b72-c2fe-470b-b10c-07720c7380d1.jpg) TCP协议中,A先向B发出一个随机数x,B收到x了以后,发给A另一个随机数y以及x+1作为答复,这样A就知道B已经收到了,因为要破解随机数x可能性并不大;然后A再发回y+1给B,这样B就知道A已经收到了。这样,A和B之间就建立一个可靠的连接,彼此相信对方已经收到并确认了信息。而事实上,A并不会知道B是否收到了y+1;并且,由于信道的不可靠性,x或者y都是可能被截获的,这些问题说明了即使是三次握手,也并不能够彻底解决两军问题,只是在现实成本可控的条件下,我们把TCP协议当作了两军问题的现实可解方法。 ## 问题形式化 只要忠诚的将军能够让别的将军接受到自己真实意图,并且所有忠诚将军能够达成一致的决定,就能解决该问题。 定义一个变量vi,作为其他将军收到的第i个将军的命令值;之后,定义一个函数来处理向量(v1,v2,…,vn),各将军用这个函数的结果作为自己最终采用的命令。 **一致性条件**:每一个忠诚的将军必须得到相同的(v1,v2,…,vn)。这意味着,忠诚的将军并不一定使用i将军送来的信息作为vi,i将军也可能是叛徒。 **正确性条件**:若i将军是忠诚的,其他忠诚的将军必须以他送出的值作为vi。 改写一致性条件如下:无论i将军是忠诚或是叛徒,任何两个忠诚的将军都使用相同的vi。这是很巧妙的一步转换,如此一致性条件)和正确性条件都只涉及一个将军i如何帮别的将军接受自己送出的值vi,所以可以将问题改为司令-副官模式来简化问题,即一个司令把自己的命令传递给n-1个副官。 **IC1:**所有忠诚的副官遵守一个命令,即一致性。 **IC2:**若司令是忠诚的,每一个忠诚的副官遵守他发出的命令,即正确性。 司令-副官模式只要将司令遍历各个将军,就可以变成完整问题,而他们采用的算法可以是完全一致的。在这种模式下,司令副官的形式下达成的一致意味着司令的命令得到了有效传达,若出现了异议,有异议的将军会作为司令发起新的司令副官模式寻求自己的观点表达,以协商达成一致。 有两种解决方法:口头协议和书面协议。 **4.4 口头协议** 口头协议满足以下条件: **A1:**每个被发送的消息都能够被正确的投递 **A2:**信息接收者知道是谁发送的消息 **A3:**能够知道缺少的消息 **4.4.1 OM(m)** 采用口头协议,若叛徒数少于1/3,则拜占庭将军问题可解。这个结论说明了,对于拜占庭将军问题,由于叛徒可以做出各种各样的判断,就必须四模冗余系统才足够容错。 **OM(0)算法** **(1)**司令将他的命令发送给每个副官。 **(2)**每个副官采用从司令发来的命令;如果没有收到命令,则默认为撤退命令。 **OM(m)算法** **(1)**司令将他的命令发送给每个副官。 **(2)**对于每个i,vi是每个副官i从司令收到的命令,如果没有收到命令,则默认为撤退命令。副官i在OM(m-1) 中作为发令者将之发送给另外n-2 个副官。 **(3)**对于每个i,和每个j ≠ i,vj 是副官i 从第2步中的副官j (使用OM(m-1)算法)发送过来的命令,如果没有收到第2步中副官j 的命令,则默认为撤退命令。最后副官i 使用majority(v1,…,vn-1)得到命令。 这个算法是一个递归算法,在OM(m)中需要采用OM(m-1)得到相关结果。m代表的是叛徒数量,从m到0,意味着对于每个将军,需要m+1轮的算法才能完成。 该算法是关于m的,意味着使用该算法必须知道有多少个叛徒。 对于任意k