lecture 7:木筏(3)-快照,线性化,重复检测
本讲座:
木筏快照
线性度
重复的RPC
更快得到
***筏日志压缩和快照(实验3B)
问题:
日志将变得巨大-比状态机状态大得多!
重新启动或发送到新服务器将花费很长时间
幸运的是:
服务器不需要既完整的日志又服务状态
在状态中捕获日志的执行部分
客户只看到状态,而不是日志
服务状态通常要小得多,所以让我们保持
服务器不能丢弃哪些条目?
未执行的条目-尚未反映在状态中
未提交的条目-可能是领导者多数的一部分
解决方案:服务定期创建持久的“快照”
[图:服务状态,磁盘快照,筏日志,筏持久]
执行特定日志条目时的服务状态副本
例如k / v表
服务将快照写入持久性存储(磁盘)
服务告诉Raft通过一些日志索引对其进行快照
筏在该索引之前丢弃日志
服务器可以随时创建快照并丢弃日志前缀
例如,当日志增长太长时
崩溃+重新启动后会发生什么?
服务从磁盘读取快照
木筏从磁盘读取持久日志
筏日志可能在快照之前开始(但绝对不会在快照之后开始)
筏会(重新)在applyCh上发送已提交的条目
因为applyIndex在重新启动后从零开始
服务将看到重复,必须检测重复的索引,忽略
如果跟随者的日志在领导者的日志开始之前结束怎么办?
nextIndex [i]将备份到领导者日志的开始
因此领导者无法使用AppendEntries RPC修复该跟随者
因此,InstallSnapshot RPC
InstallSnapshot RPC中有什么?图12、13
术语
lastIncludedIndex
lastIncludedTerm
快照数据
带InstallSnapshot的关注者做什么?
忽略术语是否旧(不是当前领导者)
忽略关注者是否已经具有上一个包含的索引/词条
这是旧的/延迟的RPC
如果不忽略:
清空日志,替换为伪造的“ prev”条目
将lastApplied设置为lastIncludedIndex
用快照内容替换服务状态(例如k / v表)
哲学注释:
状态通常等同于操作历史
您通常可以选择要存储或通信的那个
我们将在本课程的稍后部分看到这种双重性的示例
实用说明:
筏层和服务层协作以保存/恢复快照
如果状态很小,Raft的快照方案是合理的
对于大型数据库,例如,如果复制千兆字节的数据,则效果不佳
创建和写入磁盘速度慢
也许服务数据应该存在于B树中的磁盘上
无需显式持久,因为已经在磁盘上
但是,处理滞后的副本很困难
领导者应将日志保存一段时间
或保存更新记录的标识符
新服务器需要整个状态
***线性化
我们需要为实验3&c定义“正确”
客户应该如何期望“卖出”行为?
通常称为一致性合同
帮助我们思考如何正确处理复杂情况
例如并发,副本,失败,RPC重传,
领导者变更,优化
我们将在6.824中看到许多一致性定义
例如,大三角帆的时间轴一致性
“线性化”是最常见和直观的定义
形式化单个服务器的预期行为
线性度定义:
执行历史可线性化
可以找到所有操作的总顺序,
实时匹配(针对非重叠操作),并且
其中每个读取都从
按顺序在其前面写。
历史记录是客户操作的记录,每个操作
参数,返回值,开始时间,完成时间
范例1:
| -Wx1- | | -Wx2- |
| — Rx2 — |
| -Rx1- |
“ Wx1”表示“写入值1以记录x”
“ Rx1”表示“读取记录x产生的值1”
顺序:Wx1 Rx1 Wx2 Rx2
订单服从值约束(W-> R)
订单遵守实时限制(Wx1-> Wx2)
所以历史是线性的
范例2:
| -Wx1- | | -Wx2- |
| --Rx2-- |
| -Rx1- |
Wx2然后是Rx2(值),Rx2然后是Rx1(时间),Rx1然后是Wx2(值)。但
这是一个循环-因此无法将其转换为线性顺序。所以这
无法线性化。
范例3:
| --Wx0-- | | --Wx1-- |
| --Wx2-- |
| -Rx2- | | -Rx1- |
顺序:Wx0 Wx2 Rx2 Wx1 Rx1
所以它是线性的。
请注意,该服务可以选择并发写入的顺序。
例如,筏在日志中放置并发操作。
范例4:
| --Wx0-- | | --Wx1-- |
| --Wx2-- |
C1:| -Rx2- | | -Rx1- |
C2:| -Rx1- | | -Rx2- |
我们必须能够将所有操作整合到一个订单中
可能:Wx2 C1:Rx2 Wx1 C1:Rx1 C2:Rx1
但是在哪里放C2:Rx2?
必须及时到达C2:Rx1之后
但是它应该具有读取值1
没有任何命令会起作用:
C1的读取需要Wx1之前的Wx2
C2的读取在Wx2之前需要Wx1
这是一个周期,所以没有顺序
无法线性化!
因此:所有客户端必须以相同顺序查看并发写入
示例5:
忽略最近的写入是不可线性化的
Wx1 Rx1
2倍
这排除了脑裂,并忘记了已完成的写作
您可能会发现此页面有用:
https://www.anishathalye.com/2017/06/04/testing-distributed-systems-for-linearizability/
***重复RPC检测(实验3)
如果Put或Get RPC超时,客户端应该怎么做?
即Call()返回false
如果服务器已死,或请求被丢弃:重新发送
如果服务器已执行但请求丢失:重新发送很危险
问题:
这两种情况在客户端看起来都一样(无回复)
如果已经执行,客户端仍然需要结果
想法:重复的RPC检测
让我们让k / v服务检测重复的客户端请求
客户端为每个请求选择一个ID,并在RPC中发送
在相同的RPC中重新发送相同的ID
k / v服务维护按ID索引的表
为每个RPC输入一个条目
执行后记录值
如果第二个RPC使用相同的ID到达,则重复
从表中的值生成答复
设计难题:
什么时候可以(如果有)删除表条目?
如果新的领导者接手,它将如何获得重复表?
如果服务器崩溃,如何还原其表?
保持重复表较小的想法
每个客户端一个表项,而不是每个RPC一个表项
每个客户端一次只有一个未完成的RPC
每个客户端按顺序编号RPC
服务器收到客户端RPC#10时,
它可以忘记客户的较低条目
因为这意味着客户端永远不会重新发送旧的RPC
一些细节:
每个客户都需要一个唯一的客户ID-可能是64位随机数
客户端在每个RPC中发送客户端ID和seq#
如果重新发送,则重复seq#
客户端ID索引的k / v服务中的重复表
仅包含seq#,如果已执行,则包含值
RPC处理程序首先检查表,如果seq#>表项,则仅检查Start()
每个日志条目必须包含客户端ID,seq#
当操作出现在applyCh上时
更新客户端表条目中的seq#和值
唤醒等待的RPC处理程序(如果有)
如果重复请求在原始执行之前到达该怎么办?
可以再次调用Start()
它可能会在日志中出现两次(相同的客户端ID,相同的seq#)
当cmd出现在applyCh上时,如果表说已经看到,则不要执行
新的领导者如何获得重复表?
所有副本都应在执行时更新其重复表
因此,如果他们成为领导者,信息已经存在
如果服务器崩溃,如何恢复其表?
如果没有快照,则将重播日志
如果是快照,则快照必须包含表的副本
可是等等!
k / v服务器现在从重复表中返回旧值
如果表中的回复值过旧怎么办?
这可以吗?
例:
C1 C2
放(x,10)
首先发送get(x),删除10条回复
放(x,20)
重新发送get(x),从表中获取10,而不是20
linearizabilty说什么?
C1:| -Wx10- | | -Wx20- |
C2:| -Rx10 ------------- |
订购:Wx10 Rx10 Wx20
所以:返回记忆值10是正确的
***只读操作(第8节结束)
问:筏负责人是否必须在以下位置提交只读操作?
回复之前的日志?例如获取(密钥)?
也就是说,领导者可以使用以下命令立即响应Get()吗?
其键/值表的当前内容?
答:不可以,图2或实验室中的方案不适用。
假设S1认为它是领导者,并收到Get(k)。
它可能最近失去了一次选举,但没有意识到,
由于丢失了网络数据包。
新的领导者,例如S2,可能已经处理了密钥的Put(),
因此S1的键/值表中的值是过时的。
服务过时的数据无法线性化;这是裂脑。
因此:图2要求将Get()提交到日志中。
如果领导者能够提交Get(),那么(在那一点上
在日志中)仍然是领导者。对于S1
以上,在不知不觉中失去了领导地位,这不会
能够获得大多数肯定的AppendEntries答复
需要提交Get(),因此它不会回复客户端。
但是:许多应用程序都是繁重的。提交Get()s
需要时间。有什么办法可以避免提交
对于只读操作?这是一个巨大的考虑
实用系统。
想法:租赁
修改Raft协议,如下所示
定义租用期限,例如5秒
领导者每次获得AppendEntries多数票后,
它有权响应只读请求
租约期,不提交只读请求
到日志,即不发送AppendEntries。
新领导者无法执行Put(),直到之前的租约期
已过期
因此关注者可以跟踪他们上次的回复
到AppendEntries,并告诉新的领导者(在
RequestVote回复)。
结果:更快的只读操作,仍可线性化。
注意:对于实验室,您应该将Get()提交到日志中;
不要实施租赁。
可选的Spinnaker可使读取速度更快,从而降低线性度
不需要大三角帆的“时间轴读取”来反映最近的写入
他们被允许返回一个旧的(尽管已提交)值
大三角帆利用这种自由来加快读取速度:
任何副本都可以回复读取,从而允许读取负载并行化
但时间轴读取无法线性化:
副本可能未听过最近的写信
副本可以从领导者分区!
在实践中,人们经常(但并非总是)愿意过时
数据换来更高的性能