MIT6.824/lecture-10-cloud-replicated-db-aurora/10.6-quorum-fu-zhi-ji-zhi-quorum-replication.md

44 lines
7.0 KiB
Markdown
Raw Permalink Normal View History

2022-01-25 10:41:31 +08:00
# 10.6 Quorum 复制机制Quorum Replication
Aurora使用了Quorum这种思想。接下来我将描述一下经典的Quorum思想它最早可以追溯到1970年代。Aurora使用的是一种经典quorum思想的变种。Quorum系统背后的思想是通过复制构建容错的存储系统并确保即使有一些副本故障了读请求还是能看到最近的写请求的数据。通常来说Quorum系统就是简单的读写系统支持Put/Get操作。它们通常不直接支持更多更高级的操作。你有一个对象你可以读这个对象也可以通过写请求覆盖这个对象的数值。
假设有N个副本。为了能够执行写请求必须要确保写操作被W个副本确认W小于N。所以你需要将写请求发送到这W个副本。如果要执行读请求那么至少需要从R个副本得到所读取的信息。这里的W对应的数字称为Write QuorumR对应的数字称为Read Quorum。这是一个典型的Quorum配置。
![](<../.gitbook/assets/image (335).png>)
这里的关键点在于W、R、N之间的关联。Quorum系统要求任意你要发送写请求的W个服务器必须与任意接收读请求的R个服务器有重叠。这意味着R加上W必须大于N 至少满足R + W = N + 1 这样任意W个服务器至少与任意R个服务器有一个重合。
![](<../.gitbook/assets/image (336).png>)
假设你有3个服务器并且假设每个服务器只存了一个对象。
![](<../.gitbook/assets/image (337).png>)
我们发送了一个写请求想将我们的对象设置成23。为了能够执行写请求我们需要至少将写请求发送到W个服务器。我们假设在这个系统中R和W都是2N是3。为了执行一个写请求我们需要将新的数值23发送到至少2个服务器上。所以或许我们的写请求发送到了S1和S3。所以它们现在知道了我们对象的数值是23。
![](<../.gitbook/assets/image (338).png>)
如果某人发起读请求读请求会至少检查R个服务器。在这个配置中R也是2。这里的R个服务器可能包含了并没有看到之前写请求的服务器S2但同时也至少还需要一个其他服务器来凑齐2个服务器。这意味着任何读请求都至少会包含一个看到了之前写请求的服务器。
![](<../.gitbook/assets/image (339).png>)
这是Quorum系统的要求Read Quorum必须至少与Write Quorum有一个服务器是重合的。所以任何读请求可以从至少一个看见了之前写请求的服务器得到回复。
这里还有一个关键的点客户端读请求可能会得到R个不同的结果现在的问题是客户端如何知道从R个服务器得到的R个结果中哪一个是正确的呢通过不同结果出现的次数来投票Vote在这是不起作用的因为我们只能确保Read Quorum必须至少与Write Quorum有一个服务器是重合的这意味着客户端向R个服务器发送读请求可能只有一个服务器返回了正确的结果。对于一个有6个副本的系统可能Read Quorum是4那么你可能得到了4个回复但是只有一个与之前写请求重合的服务器能将正确的结果返回所以这里不能使用投票。在Quorum系统中使用的是版本号Version。所以每一次执行写请求你需要将新的数值与一个增加的版本号绑定。之后客户端发送读请求从Read Quorum得到了一些回复客户端可以直接使用其中的最高版本号的数值。
假设刚刚的例子中S2有一个旧的数值20。每一个服务器都有一个版本号S1和S3是版本3因为它们看到了相同的写请求所以它们的版本号是相同的。同时我们假设没有看到前一个写请求的S2的版本号是2。
![](<../.gitbook/assets/image (340).png>)
之后客户端从S2和S3读取数据得到了两个不同结果它们有着不同的版本号客户端会挑选版本号最高的结果。
如果你不能与Quorum数量的服务器通信不管是Read Quorum还是Write Quorum那么你只能不停的重试了。这是Quorum系统的规则你只能不停的重试直到服务器重新上线或者重新联网。
相比Chain Replication这里的优势是可以轻易的剔除暂时故障、失联或者慢的服务器。实际上这里是这样工作的当你执行写请求时你会将新的数值和对应的版本号给所有N个服务器但是只会等待W个服务器确认。类似的对于读请求你可以将读请求发送给所有的服务器但是只等待R个服务器返回结果。因为你只需要等待R个服务器这意味着在最快的R个服务器返回了之后你就可以不用再等待慢服务器或者故障服务器超时。这里忽略慢服务器或者挂了的服务器的机制完全是隐式的。在这里我们不用决定哪个服务器是在线或者是离线的只要Quorum能达到系统就能继续工作所以我们可以非常平滑的处理慢服务或者挂了的服务。
除此之外Quorum系统可以调整读写的性能。通过调整Read Quorum和Write Quorum可以使得系统更好的支持读请求或者写请求。对于前面的例子我们可以假设Write Quorum是3每一个写请求必须被所有的3个服务器所确认。这样的话Read Quorum可以只是1。所以如果你想要提升读请求的性能在一个3个服务器的Quorum系统中你可以设置R为1W为3这样读请求会快得多因为它只需要等待一个服务器的结果但是代价是写请求执行的比较慢。如果你想要提升写请求的性能可以设置R为3W为1这意味着可能只有1个服务器有最新的数值但是因为客户端会咨询3个服务器3个服务器其中一个肯定包含了最新的数值。
当R为1W为3时写请求就不再是容错的了同样当R为3W为1时读请求不再是容错的因为对于读请求所有的服务器都必须在线才能执行成功。所以在实际场景中你不会想要这么配置你或许会与Aurora一样使用更多的服务器将N变大然后再权衡Read Quorum和Write Quorum。
为了实现上一节描述的Aurora的容错目标也就是在一个AZ完全下线时仍然能写在一个AZ加一个其他AZ的服务器下线时仍然能读Aurora的Quorum系统中N=6W=4R=3。W等于4意味着当一个AZ彻底下线时剩下2个AZ中的4个服务器仍然能完成写请求。R等于3意味着当一个AZ和一个其他AZ的服务器下线时剩下的3个服务器仍然可以完成读请求。当3个服务器下线了系统仍然支持读请求仍然可以返回当前的状态但是却不能支持写请求。所以当3个服务器挂了现在的Quorum系统有足够的服务器支持读请求并据此重建更多的副本但是在新的副本创建出来替代旧的副本之前系统不能支持写请求。同时如我之前解释的Quorum系统可以剔除暂时的慢副本。