Files
MIT6.824/lecture-03-gfs/3.7-xie-wen-jian-write-file2.md
2022-01-25 02:41:31 +00:00

9.0 KiB
Raw Permalink Blame History

3.7 GFS写文件Write File2

这一部分主要是对写文件操作的问答。

学生提问写文件失败之后Primary和Secondary服务器上的状态如何恢复

Robert教授你的问题是Primary告诉所有的副本去执行数据追加操作某些成功了某些没成功。如果某些副本没有成功执行Primary会回复客户端说执行失败。之后客户端会认为数据没有追加成功。但是实际上部分副本还是成功将数据追加了。所以现在一个Chunk的部分副本成功完成了数据追加而另一部分没有成功这种状态是可接受的没有什么需要恢复这就是GFS的工作方式。

学生提问写文件失败之后读Chunk数据会有什么不同

Robert教授如果写文件失败之后一个客户端读取相同的Chunk客户端可能可以读到追加的数据也可能读不到取决于客户端读的是Chunk的哪个副本。

如果一个客户端发送写文件的请求并且得到了表示成功的回复那意味着所有的副本都在相同的位置追加了数据。如果客户端收到了表示失败的回复那么意味着0到多个副本实际追加了数据其他的副本没有追加上数据。所以这时有些副本会有追加的数据有些副本没有。这时取决于你从哪个副本读数据有可能读到追加的新数据也有可能读不到。

学生提问:可不可以通过版本号来判断副本是否有之前追加的数据?

Robert教授所有的Secondary都有相同的版本号。版本号只会在Master指定一个新Primary时才会改变。通常只有在原Primary发生故障了才会指定一个新的Primary。所以副本参与写操作的Primary和Secondary都有相同的版本号你没法通过版本号来判断它们是否一样或许它们就是不一样的取决于数据追加成功与否

这么做的理由是当Primary回复“no”给客户端时客户端知道写入失败了之后客户端的GFS库会重新发起追加数据的请求直到最后成功追加数据。成功了之后追加的数据会在所有的副本中相同位置存在。在那之前追加的数据只会在部分副本中存在。

学生提问:客户端将数据拷贝给多个副本会不会造成瓶颈?

Robert教授这是一个好问题。考虑到底层网络写入文件数据的具体传输路径可能会非常重要。当论文第一次提到这一点时它说客户端会将数据发送给每个副本。实际上之后论文又改变了说法说客户端只会将数据发送给离它最近的副本之后那个副本会将数据转发到另一个副本以此类推形成一条链直到所有的副本都有了数据。这样一条数据传输链可以在数据中心内减少跨交换机传输否则所有的数据吞吐都在客户端所在的交换机上

学生提问:什么时候版本号会增加?

Robert教授版本号只在Master节点认为Chunk没有Primary时才会增加。在一个正常的流程中如果对于一个Chunk来说已经存在了Primary那么Master节点会记住已经有一个Primary和一些SecondaryMaster不会重新选择Primary也不会增加版本号。它只会告诉客户端说这是Primary并不会变更版本号。

学生提问:如果写入数据失败了,不是应该先找到问题在哪再重试吗?

Robert教授我认为这是个有趣的问题。当Primary向客户端返回写入失败时你或许会认为一定是哪里出错了在修复之前不应该重试。实际上就我所知论文里面在重试追加数据之前没有任何中间操作。因为错误可能就是网络数据的丢失这时就没什么好修复的网络数据丢失了我们应该重传这条网络数据。客户端重新尝试追加数据可以看做是一种复杂的重传数据的方法。或许对于大多数的错误来说我们不需要修改任何东西同样的Primary同样的Secondary客户端重试一次或许就能正常工作因为这次网络没有丢包。

但是如果是某一个Secondary服务器出现严重的故障那问题变得有意思了。我们希望的是Master节点能够重新生成Chunk对应的服务器列表将不工作的Secondary服务器剔除再选择一个新的Primary并增加版本号。如果这样的话我们就有了一组新的PrimarySecondary和版本号同时我们还有一个不太健康的Secondary它包含的是旧的副本和旧的版本号正是因为版本号是旧的Master永远也不会认为它拥有新的数据。但是论文中没有证据证明这些会立即发生。论文里只是说客户端重试并且期望之后能正常工作。最终Master节点会ping所有的Chunk服务器如果Secondary服务器挂了Master节点可以发现并更新Primary和Secondary的集合之后再增加版本号。但是这些都是之后才会发生而不是立即发生

学生提问如果Master节点发现Primary挂了会怎么办

Robert教授可以这么回答这个问题。在某个时间点Master指定了一个Primary之后Master会一直通过定期的ping来检查它是否还存活。因为如果它挂了Master需要选择一个新的Primary。Master发送了一些ping给Primary并且Primary没有回应你可能会认为Master会在那个时间立刻指定一个新的Primary。但事实是这是一个错误的想法。为什么是一个错误的想法呢因为可能是网络的原因导致ping没有成功所以有可能Primary还活着但是网络的原因导致ping失败了。但同时Primary还可以与客户端交互如果Master为Chunk指定了一个新的Primary那么就会同时有两个Primary处理写请求这两个Primary不知道彼此的存在会分别处理不同的写请求最终会导致有两个不同的数据拷贝。这被称为脑裂split-brain

脑裂是一种非常重要的概念我们会在之后的课程中再次介绍它详见6.1它通常是由网络分区引起的。比如说Master无法与Primary通信但是Primary又可以与客户端通信这就是一种网络分区问题。网络故障是这类分布式存储系统中最难处理的问题之一。

所以我们想要避免错误的为同一个Chunk指定两个Primary的可能性。Master采取的方式是当指定一个Primary时为它分配一个租约Primary只在租约内有效。Master和Primary都会知道并记住租约有多长当租约过期了Primary会停止响应客户端请求它会忽略或者拒绝客户端请求。因此如果Master不能与Primary通信并且想要指定一个新的Primary时Master会等到前一个Primary的租约到期。这意味着Master什么也不会做只是等待租约到期。租约到期之后可以确保旧的Primary停止了它的角色这时Master可以安全的指定一个新的Primary而不用担心出现这种可怕的脑裂的情况。

学生提问为什么立即指定一个新的Primary是坏的设计既然客户端总是先询问Master节点Master指定完Primary之后将新的Primary返回给客户端不行吗

Robert教授因为客户端会通过缓存提高效率客户端会在短时间缓存Primary的身份信息这样客户端就不用每次都会向Master请求Primary信息。即使没有缓存也可能出现这种情况客户端向Master节点查询Primary信息Master会将Primary信息返回这条消息在网络中传播。之后Master如果发现Primary出现故障并且立刻指定一个新的Primary同时向新的Primary发消息说你是Primary。Master节点之后会向其他查询Primary的客户端返回这个新的Primary。而前一个Primary的查询还在传递过程中前一个客户端收到的还是旧的Primary的信息。如果没有其他的更聪明的一些机制前一个客户端是没办法知道收到的Primary已经过时了。如果前一个客户端执行写文件那么就会与后来的客户端产生两个冲突的副本。

学生提问:如果是对一个新的文件进行追加,那这个新的文件没有副本,会怎样?

Robert教授你会按照黑板上的路径见3.6再执行一遍。Master会从客户端收到一个请求说我想向这个文件追加数据。我猜Master节点会发现该文件没有关联的Chunk。Master节点或许会通过随机数生成器创造一个新的Chunk ID。之后Master节点通过查看自己的Chunk表单发现自己其实也没有Chunk ID对应的任何信息。之后Master节点会创建一条新的Chunk记录说我要创建一个新的版本号为1再随机选择一个Primary和一组Secondary并告诉它们你们将对这个空的Chunk负责请开始工作。论文里说每个Chunk默认会有三个副本所以通常来说是一个Primary和两个Secondary。