This commit is contained in:
Vonng 2018-03-30 02:05:28 +08:00
parent 11e8c80a64
commit 849eefcc24

104
ch9.md
View File

@ -630,7 +630,7 @@ CAP定理的正式定义仅限于很狭隘的范围【30】它只考虑了一
这个过程有点像西方传统婚姻仪式司仪分别询问新娘和新郎是否要结婚通常是从两方都收到“我愿意”的答复。收到两者的回复后司仪宣布这对情侣成为夫妻事务就提交了这一幸福事实会广播至所有的参与者中。如果新娘与新郎之一没有回复”我愿意“婚礼就会中止【73】。
#### 承诺系统
#### 系统承诺
这个简短的描述可能并没有说清楚为什么两阶段提交保证了原子性,而跨多个节点的一阶段提交却没有。在两阶段提交的情况下,准备请求和提交请求当然也可以轻易丢失。 2PC又有什么不同呢
@ -674,127 +674,127 @@ CAP定理的正式定义仅限于很狭隘的范围【30】它只考虑了一
### 实践中的分布式事务
分布式事务,尤其是那些通过两阶段提交实现的事务,毁誉参半。一方面,它们被看作是提供一个难以实现的重要的安全保证;另一方面,他们被批评为造成运维问题,造成性能下降,承诺超过他们能够实现的目标【81,82,83,84】。许多云服务由于其导致的运维问题而选择不实现分布式事务【85,86】。
分布式事务的名声毁誉参半,尤其是那些通过两阶段提交实现的。一方面,它被视作提供了一个难以实现的重要的安全性保证;另一方面,它们因为导致运维问题,造成性能下降,做出超过能力范围的承诺而饱受批评【81,82,83,84】。许多云服务由于其导致的运维问题而选择不实现分布式事务【85,86】。
分布式事务的某些实现会带来严重的性能损失—— 例如MySQL中的分布式事务被报告比单节点事务慢10倍以上【87】所以当人们建议不要使用这些事务时就不足为奇了。两阶段提交所固有的大部分性能成本是由于崩溃恢复所需的额外强制刷盘(`fsync`【88】以及额外的网络往返。
分布式事务的某些实现会带来严重的性能损失 —— 例如据报告称MySQL中的分布式事务比单节点事务慢10倍以上【87】所以当人们建议不要使用它们时就不足为奇了。两阶段提交所固有的性能成本,大部分是由于崩溃恢复所需的额外强制刷盘(`fsync`【88】以及额外的网络往返。
是,我们不应该直接抛弃分布式事务,而应该更加仔细地审视这些事务,因为从中可以汲取重要的经验教训。首先,我们应该精确地说明“**分布式事务**”的含义。两种截然不同的分布式事务类型经常被混淆:
我们不应该直接忽视分布式事务,而应当更加仔细地审视这些事务,因为从中可以汲取重要的经验教训。首先,我们应该精确地说明“**分布式事务**”的含义。两种截然不同的分布式事务类型经常被混淆:
***数据库内部的分布式事务***
一些分布式数据库(即在其标准配置中使用复制和分区的数据库)支持数据库节点之间的内部事务。例如VoltDB和MySQL Cluster的NDB存储引擎就有这样的内部事务支持。在这种情况下所有参与事务的节点都运行相同的数据库软件。
一些分布式数据库即在其标准配置中使用复制和分区的数据库支持数据库节点之间的内部事务。例如VoltDB和MySQL Cluster的NDB存储引擎就有这样的内部事务支持。在这种情况下所有参与事务的节点都运行相同的数据库软件。
***异构分布式事务***
异构事务中,参与者有两种或两种以上不同的技术:例如来自不同供应商的两个数据库,甚至是非数据库系统(如消息代理)。跨系统的分布式事务必须确保原子提交,尽管系统可能完全不同。
**异构heterogeneous**事务中,参与者是两种或以上不同技术:例如来自不同供应商的两个数据库,甚至是非数据库系统(如消息代理)。跨系统的分布式事务必须确保原子提交,尽管系统可能完全不同。
数据库内部事务不必与任何其他系统兼容,因此他们可以使用任何协议并应用特定技术的特定优化。因此,数据库内部的分布式事务通常工作地很好。另一方面,跨异构技术的事务则更有挑战性。
数据库内部事务不必与任何其他系统兼容,因此它们可以使用任何协议,并能针对特定技术进行特定的优化。因此数据库内部的分布式事务通常工作地很好。另一方面,跨异构技术的事务则更有挑战性。
#### 恰好一次的消息处理
异构的分布式事务处理能够以强大的方式集成不同的系统。例如,当且仅当用于处理消息的数据库事务处理时,来自消息队列的消息才能被确认为已处理成功承诺。这是通过自动提交消息确认和数据库写入单个事务来实现的。使用分布式事务支持,即使消息代理和数据库是在不同机器上运行的两个不相关技术,也是可能的。
异构的分布式事务处理能够以强大的方式集成不同的系统。例如:消息队列中的一条消息可以被确认为已处理,当且仅当用于处理消息的数据库事务成功提交。这是通过在同一个事务中原子提交**消息确认**和**数据库写入**两个操作来实现的。藉由分布式事务的支持,即使消息代理和数据库是在不同机器上运行的两种不相关的技术,这种操作也是可能的。
如果消息传递或数据库事务失败,两者都会中止,因此消息代理可能会稍后安全地重新传递消息。因此,通过自动提交消息及其处理的副作用,即使在成功之前需要几次重试,也可以确保消息被有效处理一次。中止放弃部分完成的事务的任何副作用。
如果消息传递或数据库事务任意一者失败,两者都会中止,因此消息代理可能会在稍后安全地重传消息。因此,通过原子提交**消息处理及其副作用**,即使在成功之前需要几次重试,也可以确保消息被**有效地effectively**恰好处理一次。中止会抛弃部分完成事务所导致的任何副作用。
这样的分布式事务只有在所有受事务影响的系统都能够使用相同的原子提交协议的情况下才是可能的。例如,处理消息的副作用是发送邮件,而邮件服务器不支持两阶段提交:如果邮件处理失败并重试,可能会发送两次或更多次的邮件。但是,如果处理消息的所有副作用在事务中止时回滚,那么可以安全地重新尝试处理步骤,就好像什么都没发生过一样。
然而,只有当所有受事务影响的系统都使用同样的**原子提交协议atomic commit protocl**时,这样的分布式事务才是可能的。例如,假设处理消息的副作用是发送一封邮件,而邮件服务器并不支持两阶段提交:如果消息处理失败并重试,则可能会发送两次或更多次的邮件。但如果处理消息的所有副作用都可以在事务中止时回滚,那么这样的处理流程就可以安全地重试,就好像什么都没有发生过一样。
我们将在[第11章](ch11.md)中再次回到”恰好一次“消息处理的主题。让我们先看看允许这种异构分布式事务的原子提交协议。
在[第11章](ch11.md)中再次回到”恰好一次“消息处理的主题。让我们先看看允许这种异构分布式事务的原子提交协议。
#### XA事务
*X/Open XA***扩展架构eXtended Architecture**的缩写是跨异构技术实现两阶段提交的标准【76,77】。它于1991年推出并得到了广泛的实许多传统关系数据库包括PostgreSQLMySQLDB2SQL Server和Oracle和消息代理包括ActiveMQHornetQMSMQ和IBM MQ
*X/Open XA***扩展架构eXtended Architecture**的缩写是跨异构技术实现两阶段提交的标准【76,77】。它于1991年推出并得到了广泛的实许多传统关系数据库包括PostgreSQLMySQLDB2SQL Server和Oracle和消息代理包括ActiveMQHornetQMSMQ和IBM MQ 都支持XA
XA不是一个网络协议——它只是一个用于与事务协调者连接的C API。此API的绑定以其他语言存在例如在Java EE应用程序的世界中XA事务是使用Java事务APIJTA实现的而Java事务APIJTA则由许多用于使用Java数据库连接JDBC的数据库驱动程序以及使用Java消息服务JMSAPI
XA不是一个网络协议——它只是一个用来与事务协调者连接的C API。其他语言也有这种API的绑定例如在Java EE应用的世界中XA事务是使用**Java事务APIJTA, Java Transaction API**实现的,而许多使用**Java数据库连接JDBC, Java Database Connectivity**的数据库驱动,以及许多使用**Java消息服务JMS**API的消息代理都支持**Java事务APIJTA**
XA假定你的应用程序使用网络驱动程序或客户端库来与参与者数据库或消息传递服务进行通信。如果驱动程序支持XA则表示它调用XA API以查明操作是否应该是分布式事务的一部分——如果是则将必要的信息发送到数据库服务器。驱动还会提供回调,协调者可以通过回调来要求参与者准备,提交或中止。
XA假定你的应用使用网络驱动或客户端库来与**参与者**进行通信数据库或消息服务。如果驱动支持XA则意味着它会调用XA API 以查明操作是否为分布式事务的一部分 —— 如果是,则将必要的信息发往数据库服务器。驱动还会向协调者暴露回调接口,协调者可以通过回调来要求参与者准备,提交或中止。
事务协调者实现XA API。标准没有指定应该如何实现但实际上协调者通常只是一个库与发出事务的应用程序不是单独的服务一起被加载到相同的进程中。它跟踪事务的参与者在要求他们准备通过回调驱动程序之后收集参与者的回答并使用本地磁盘上的日志记录每次事务的提交/中止决定
事务协调者需要实现XA API。标准没有指明应该如何实现但实际上协调者通常只是一个库被加载到发起事务的应用的同一个进程中而不是单独的服务。它在事务中个跟踪所有的参与者并在要求它们**准备**之后收集参与者的响应(通过驱动回调),并使用本地磁盘上的日志记录每次事务的决定(提交/中止)
如果应用程序进程崩溃,或者运行应用程序的机器死亡,协调者就会使用它。然后任何有准备但未提交的事务的参与者都被怀疑。由于协调程序的日志位于应用程序服务器的本地磁盘上,因此必须重新启动该服务器,并且协调程序库必须读取日志以恢复每个事务的提交/中止结果。只有这样,协调者才能使用数据库驱动程序的XA回调来要求参与者提交或中止。数据库服务器不能直接联系协调者因为所有通信都必须通过客户端库。
如果应用进程崩溃,或者运行应用的机器报销了,协调者也随之往生极乐。然后任何带有**准备了**但未提交事务的参与者都会在疑虑中卡死。由于协调程序的日志位于应用服务器的本地磁盘上,因此必须重启该服务器,且协调程序库必须读取日志以恢复每个事务的提交/中止结果。只有这样协调者才能使用数据库驱动的XA回调来要求参与者提交或中止。数据库服务器不能直接联系协调者因为所有通信都必须通过客户端库。
#### 怀疑时持有锁
为什么我们非常关心存疑事务?系统的其他部分不能继续工作,而忽视最终将被清理的有问题的事务吗?
为什么我们这么关心存疑事务?系统的其他部分就不能继续正常工作,无视那些终将被清理的存疑事务吗?
问题在于**锁**。正如在“[读已提交](ch7.md#读已提交)”中所讨论的那样,数据库事务通常对其修改的行进行行级别的排他锁定,以防止脏写。此外,如果要使用可序列化的隔离等级,则使用两阶段锁定的数据库也必须对事务读取的任何行执行共享锁定(参见“[两阶段锁定2PL](ch7.md#两阶段锁定2PL)”)。
问题在于**锁locking**。正如在“[读已提交](ch7.md#读已提交)”中所讨论的那样,数据库事务通常获取待修改的行上的**行级排他锁**,以防止脏写。此外,如果要使用可序列化的隔离等级,则使用两阶段锁定的数据库也必须为事务所读取的行加上共享锁(参见“[两阶段锁定2PL](ch7.md#两阶段锁定2PL)”)。
在事务提交或中止之前,数据库不能释放这些锁(如[图9-9](img/fig9-9.png)中的阴影区域所示)。因此,在使用两阶段提交时,事务必须在整个时间内保持锁定状态。如果协调者已经崩溃需要20分钟才能重这些锁将会持有20分钟。如果协调者的日志由于某种原因完全丢失,这些锁将永久保存,或者至少在管理员手动解决该情况之前。
在事务提交或中止之前,数据库不能释放这些锁(如[图9-9](img/fig9-9.png)中的阴影区域所示)。因此,在使用两阶段提交时,事务必须在整个存疑期间持有这些锁。如果协调者已经崩溃需要20分钟才能重启那么这些锁将会持有20分钟。如果协调者的日志由于某种原因彻底丢失,这些锁将被永久持有 —— 或至少在管理员手动解决该情况之前。
当这些锁被保留时,其他事务不能修改这些行。根据数据库的不同,其他事务甚至可能被阻止读取这些行。因此,其他事务不能简单地继续他们的业务 - 如果他们想访问相同的数据,他们将被阻止。这可能会导致大部分应用程序变得不可用,直到有问题的事务得到解决。
当这些锁被持有时,其他事务不能修改这些行。根据数据库的不同,其他事务甚至可能因为读取这些行而被阻塞。因此,其他事务没法儿简单地继续它们的业务了 —— 如果它们要访问同样的数据,就会被阻塞。这可能会导致应用大面积进入不可用状态,直到存疑事务被解决。
#### 从协调者故障中恢复
理论上,如果协调者崩溃并重新启动,它应该干净地从日志中恢复其状态,并解决任何有问题的事务。然而在实践中孤立的不确定事务确实发生【89,90】也就是说协调者不能以任何理由决定结果的事务例如因为事务日志已经由于软件错误。这些事务不能自动解决所以他们永远待在数据库中持有锁并阻止其他事务。
理论上,如果协调者崩溃并重新启动,它应该干净地从日志中恢复其状态,并解决任何存疑事务。然而在实践中,**孤立orphaned**的存疑事务确实会出现【89,90】即无论出于何种理由协调者无法确定事务的结果例如事务日志已经由于软件错误丢失或损坏。这些事务无法自动解决所以它们永远待在数据库中持有锁并阻塞其他事务。
即使重新启动数据库服务器也不能解决这个问题因为2PC的正确实现必须在重新启动时保留一个有问题的事务的锁否则就会冒有违反原子性保证的风险。这是一个棘手的情况。
即使重启数据库服务器也无法解决这个问题因为在2PC的正确实现中即使重启也必须保留存疑事务的锁否则就会冒有违反原子性保证的风险。这是一种棘手的情况。
唯一的出路是让管理员手动决定提交还是回滚事务。管理员必须检查每个有问题的事务的参与者,确定是否有任何参与者已经提交或中止,然后将相同的结果应用于其他参与者。解决这个问题潜在地需要大量的人力,并且可能发生在严重的生产中断期间(否则,为什么协调者处于这样一个糟糕的状态),很可能需要在高压力和时间压力下完成。
唯一的出路是让管理员手动决定提交还是回滚事务。管理员必须检查每个存疑事务的参与者,确定是否有任何参与者已经提交或中止,然后将相同的结果应用于其他参与者。解决这个问题潜在地需要大量的人力,并且可能发生在严重的生产中断期间(不然为什么协调者处于这种糟糕的状态),并很可能要在巨大精神压力和时间压力下完成。
许多XA的实现都有一个叫做**启发式决策heuristic decistions**的紧急逃生舱口:允许参与者单方面决定放弃或提交一个有疑问的事务而不需要协调者做出明确的决定【76,77,91】。要清楚的是这里的启发式是可能破坏原子性的委婉说法因为它违背了两阶段承诺的承诺体系。因此启发式决策只是为了逃出灾难性的情况而不是经常性的使用
许多XA的实现都有一个叫做**启发式决策heuristic decistions**的紧急逃生舱口:允许参与者单方面决定放弃或提交一个存疑事务而无需协调者做出最终决定【76,77,91】。要清楚的是这里**启发式**是**可能破坏原子性probably breaking atomicity**的委婉说法,因为它违背了两阶段提交的系统承诺。因此,启发式决策只是为了逃出灾难性的情况而准备的,而不是为了日常使用的
#### 分布式事务的限制
XA事务解决了保持多个参与者数据系统一致的真实而重要的问题,但正如我们所看到的那样,它们也引入了主要的运维问题。特别是,关键的实现是事务协调者本身就是一种数据库(在其中存储事务结果),因此需要像其他重要数据库一样小心:
XA事务解决了保持多个参与者(数据系统)相互一致的现实的重要问题,但正如我们所看到的那样,它也引入了严重的运维问题。特别来讲,这里的核心认识是:事务协调者本身就是一种数据库(存储了事务的结果),因此需要像其他重要数据库一样小心地打交道
* 如果协调者没有被复制,而是只在一台机器上运行,那么整个系统是一个失效的单点(因为它的失效会导致其他应用程序服务器阻塞在有问题的事务处理的锁上)。令人惊讶的是,许多协调者实现默认情况下不是高可用,或者只有基本的复制支持。
* 许多服务器端应用程序都是在无状态模式下开发的受到HTTP的青睐所有持久状态都存储在数据库中具有应用程序服务器可随意添加和删除的优点。但是当协调者是应用程序服务器的一部分时,它会改变部署的性质。突然间,协调者的日志成为持久系统状态的关键部分—— 与数据库本身一样重要,因为协调者日志是为了在崩溃后恢复疑事务所必需的。这样的应用程序服务器不再是无状态的。
* 由于XA需要与各种数据系统兼容,因此它是必须的最小公分母。例如,它不能检测到不同系统间的死锁(因为这将需要一个标准化的协议来让系统交换每个事务正在等待的锁的信息),而且它不适用于[SSI](ch7.md#可串行快照隔离SSI ),因为这需要一个协议来识别不同系统之间的冲突
* 对于数据库内部的分布式事务(而不是XA限制不是很大例如SSI的分布式版本是可能的。然而仍然存在2PC成功进行事务的问题所有参与者都必须作出回应。因此如果系统的任何部分损坏事务也会失败。因此分布式事务有扩大故障的趋势与我们构建容错系统的目标背道而驰。
* 如果协调者没有复制,而是只在单台机器上运行,那么它是整个系统的失效单点(因为它的失效会导致其他应用服务器阻塞在存疑事务持有的锁上)。令人惊讶的是,许多协调者实现默认情况下不是高可用,或者只有基本的复制支持。
* 许多服务器端应用都是使用无状态模式开发的受HTTP的青睐所有持久状态都存储在数据库中因此具有应用服务器可随意按需添加删除的优点。但是当协调者成为应用服务器的一部分时,它会改变部署的性质。突然间,协调者的日志成为持久系统状态的关键部分—— 与数据库本身一样重要,因为协调者日志是为了在崩溃后恢复疑事务所必需的。这样的应用服务器不再是无状态的
* 由于XA需要兼容各种数据系统,因此它必须是所有系统的最小公分母。例如,它不能检测不同系统间的死锁(因为这将需要一个标准协议来让系统交换每个事务正在等待的锁的信息),而且它无法与[SSI ](ch7.md#可串行快照隔离SSI )协同工作,因为这需要一个跨系统定位冲突的协议
* 对于数据库内部的分布式事务(不是XA限制没有这么大例如分布式版本的SSI 是可能的。然而仍然存在问题2PC成功提交一个事务需要所有参与者的响应。因此如果系统的**任何**部分损坏,事务也会失败。因此,分布式事务又有**扩大失效amplifying failures**的趋势,这又与我们构建容错系统的目标背道而驰。
这些事实是否意味着我们应该放弃保持几个系统一致的所有希望?不完全是——有其他的方法可以让我们在没有异构分布式事务的痛苦的情况下实现同样的事情。我们将在[第11章](ch11.md) 和[第12章](ch12.md) 回到这些章节。但首先,我们应该总结共识的话题。
这些事实是否意味着我们应该放弃保持几个系统相互一致的所有希望?不完全是 —— 还有其他的办法,可以让我们在没有异构分布式事务的痛苦的情况下实现同样的事情。我们将在[第11章](ch11.md) 和[第12章](ch12.md) 回到这些章节。但首先,我们应该概括一下关于**共识**的话题。
### 容错共识
### 容错共识
非正式地,共识意味着让几个节点达成一致。例如,如果有几个人同时尝试预订飞机上的最后一个座位或剧院中的同一个座位,或者尝试使用相同的用户名注册一个帐户,则可以使用一个一致的算法来确定哪个其中一个互不相容的行动应该是赢家。
非正式地,共识意味着让几个节点就某事达成一致。例如,如果有几个人**同时concurrently**尝试预订飞机上的最后一个座位或剧院中的同一个座位,或者尝试使用相同的用户名注册一个帐户。共识算法可以用来确定这些**互不相容mutually incompatible**的操作中,哪一个才是赢家。
共识问题通常形式化如下:一个或多个节点可以**提propose**某个值,而共识算法**决定decides**采用其中的一个值。在座位预订的例子中,当几个顾客同时试图购最后一个座位时,处理顾客请求的每个节点可以提出正在服务的顾客的ID且决定指明了哪个顾客获得座位。
共识问题通常形式化如下:一个或多个节点可以**提propose**某些值,而共识算法**决定decides**采用其中的某个值。在座位预订的例子中,当几个顾客同时试图购最后一个座位时,处理顾客请求的每个节点可以**提议**正在服务的顾客的ID而**决定**指明了哪个顾客获得了座位。
在这种形式共识算法必须满足以下性质【25】[^xiii]
在这种形式共识算法必须满足以下性质【25】[^xiii]
[^xiii]: 这种共识的特殊形式被称为统一共识,相当于在具有不可靠故障检测器的异步系统中的常规共识【71】。学术文献通常指的是过程而不是节点但我们在这里使用节点来与本书的其余部分保持一致。
[^xiii]: 这种共识的特殊形式被称为**统一共识uniform consensus**,相当于在具有不可靠故障检测器的异步系统中的**常规共识regular consensus**【71】。学术文献通常指的是**进程process**而不是节点,但我们在这里使用**节点node**来与本书的其余部分保持一致。
***一致同意***
***一致同意Uniform agreement***
没有两个节点的决定不同。
***完整性***
***完整性Integrity***
没有节点决定两次。
***有效性***
***有效性Validity***
如果一个节点决定`v`,则`v`由某个节点提出
如果一个节点决定了值 `v` ,则 `v` 由某个节点所提议
***终止***
***终止Termination***
由所有未崩溃的节点来最终决定值。
一致同意和完整性属性定义了共识的核心思想:每个人都决定了相同的结果,一旦决定了,你就不能改变主意。有效性属性主要是为了排除平凡的解决方案:例如,无论提出什么建议,都可以有一个总是决定为空的算法;该算法将满足协议和完整性属性,但不符合有效性属性。
**一致同意**和**完整性**属性定义了共识的核心思想:所有人都决定了相同的结果,一旦决定了,你就不能改变主意。**有效性**属性主要是为了排除平凡的解决方案:例如,无论提议了什么值,你都可以有一个始终决定值为`null`的算法。;该算法满足**一致同意**和**完整性**属性,但不满足**有效性**属性。
如果你不关心容错,那么满足前三个属性很容易:你可以将一个节点硬编码为“独裁者”,并让该节点做出所有的决定。但是,如果该节点失效,那么系统就不能再做出任何决定。事实上,这就是我们在两阶段提交的情况下所看到的:如果协调者失败了,那么不确定的参与者就不能决定是否提交或中止。
如果你不关心容错,那么满足前三个属性很容易:你可以将一个节点硬编码为“独裁者”,并让该节点做出所有的决定。但如果该节点失效,那么系统就无法再做出任何决定。事实上,这就是我们在两阶段提交的情况中所看到的:如果协调者失效,那么存疑的参与者就无法决定提交还是中止。
终止属性正式形成了容错的思想。它基本上说的是,一个共识算法不能简单地坐下来,永远不做任何事——换句话说,它必须取得进展。即使有些节点出现故障,其他节点也必须做出决定。 (终止是一种**活性属性**,而另外三种是安全属性——参见“[安全性和活性](ch8.md#安全性和活性)”。)
**终止**属性正式形成了容错的思想。它实质上说的是,一个共识算法不能简单地永远闲坐着等死 —— 换句话说,它必须取得进展。即使部分节点出现故障,其他节点也必须达成一项决定。 **终止**是一种**活性属性**,而另外三种是安全属性 —— 参见“[安全性和活性](ch8.md#安全性和活性)”。)
共识的系统模型假设,当一个节点“崩溃”时,它突然消失且永远不会回来。(而不是软件崩溃想象一下地震包含你的节点的数据中心被山体滑坡所摧毁你必须假设节点被埋在30英尺以下的泥土中并且永远不会回到在线状态。这个系统模型任何等待节点恢复的算法都不能满足终止属性。特别是2PC不符合终止的要求。
共识的系统模型假设,当一个节点“崩溃”时,它会突然消失而且永远不会回来。(不像软件崩溃想象一下地震包含你的节点的数据中心被山体滑坡所摧毁你必须假设节点被埋在30英尺以下的泥土中并且永远不会重新上线)在这个系统模型中,任何需要等待节点恢复的算法都不能满足**终止**属性。特别是2PC不符合终止属性的要求。
当然如果所有的节点都崩溃而且没有一个正在运行那么任何算法都不可能决定什么。算法可以容忍的失败次数有一个限制事实上可以证明任何一致性算法都需要至少大部分节点正确运行以确保终止属性【67】。大多数人可以安全地形成法定人数(参阅“[读写的法定人数](ch5.md#读写的法定人数)”)。
当然如果**所有**的节点都崩溃了,没有一个在运行,那么所有算法都不可能决定任何事情。算法可以容忍的失效数量是有限的:事实上可以证明,任何共识算法都需要至少占总体**多数majority**的节点正确工作以确保终止属性【67】。多数可以安全地组成法定人数(参阅“[读写的法定人数](ch5.md#读写的法定人数)”)。
因此,终止属性受到不到一半的节点崩溃或不可达的假设。然而,即使大多数节点出现故障或存在严重的网络问题,大多数共识的实施都能确保始终满足安全属性——同意完整性和有效性【92】。因此大规模的中断可能会阻止系统处理请求但是它不能通过使系统做出无效的决定来破坏共识系统。
因此**终止**属性取决于一个假设,**不超过一半的节点崩溃或不可达**。然而即使多数节点出现故障或存在严重的网络问题,绝大多数共识的实现都能始终确保安全属性得到满足—— 一致同意完整性和有效性【92】。因此大规模的中断可能会阻止系统处理请求但是它不能通过使系统做出无效的决定来破坏共识系统。
大多数一致性算法假定没有**拜占庭式错误**,正如在“[拜占庭式错误](#拜占庭式错误)”一节中所讨论的那样。也就是说,如果一个节点没有正确地遵循协议(例如,如果它发送矛盾的消息到不同的节点它可能会破坏协议的安全属性。只要少于三分之一的节点是拜占庭故障【25,93】就可以对拜占庭故障达成共识但我们没有地方在本书中详细讨论这些算法。
大多数共识算法假设不存在**拜占庭式错误**,正如在“[拜占庭式错误](#拜占庭式错误)”一节中所讨论的那样。也就是说,如果一个节点没有正确地遵循协议(例如,如果它向不同节点发送矛盾的消息它就可能会破坏协议的安全属性。克服拜占庭故障稳健地达成共识是可能的只要少于三分之一的节点存在拜占庭故障【25,93】。但我们没有地方在本书中详细讨论这些算法
#### 共识算法和全序广播
着名的容错一致性算法是**视图戳复制viewstamped replication**VSR【94,95】Paxos 【96,97,98,99】Raft 【22,100,101】和Zab 【15,21,102】 。这些算法之间有相当多的相似之处但它们并不相同【103】。在本书中我们不会详细介绍不同的算法只要了解一些共同的高级思想就足够了,除非你准备自己实现一个共识系统。(可能不是一个明智的做法,相当难【98,104】
著名的容错共识算法是**视图戳复制VSR, viewstamped replication**【94,95】Paxos 【96,97,98,99】Raft 【22,100,101】以及 Zab 【15,21,102】 。这些算法之间有不少相似之处但它们并不相同【103】。在本书中我们不会介绍各种算法的详细细节了解一些它们共通的高级思想通常已经足够了,除非你准备自己实现一个共识系统。(可能不明智相当难【98,104】
这些算法中的大多数实际上并不直接使用这里描述的形式化模型(建议和决定单个值,一致同意,完整性,有效性和终止性质)。相反,它们决定了值的顺序,这使得它们成为了全序广播算法,正如本章前面所讨论的那样(参阅“[全序广播](#全序广播)”)。
大多数这些算法实际上并不直接使用这里描述的形式化模型(提议与决定单个值,一致同意,完整性,有效性和终止属性)。取而代之的是,它们决定了值的**顺序sequence**,这使它们成为全序广播算法,正如本章前面所讨论的那样(参阅“[全序广播](#全序广播)”)。
请记住,全序广播要求将消息按照相同的顺序准确传送到所有节点。如果仔细思考,这相当于进行了几轮共识:在每一轮中,节点提出下一个要发送的消息,然后决定下一个要发送的消息总数【67】。
请记住,全序广播要求将消息按照相同的顺序,恰好传递一次,准确传送到所有节点。如果仔细思考,这相当于进行了几轮共识:在每一轮中,节点提议下一条要发送的消息,然后决定在全序中下一条要发送的消息【67】。
所以,全序广播相当于重复的一轮共识(每个共同的决定对应于一个消息传递
所以,全序广播相当于重复进行多轮共识(每个共识决定与一次消息传递相对应
* 由于协商一致意见,所有节点决定以相同的顺序传递相同的消息。
* 由于一致同意属性,所有节点决定以相同的顺序传递相同的消息。
* 由于完整性属性,消息不重复。