【分布式-6.824】Lecture3-GFS

目录

 

0.论文链接

1.WYH HARD?

2.问题

3.关于GFS的经典论文的基本论述

4.关于对错误数据的忍耐性

5.GFS架构与设计

6.读操作

7.写操作

8.强一致性分布式文件系统的设计思路


0.论文链接

reference-gfs.pdf

1.WYH HARD?

performance -->sharding
faults      -->tolerance
repl        -->inconsistency
consistency -->low performance 

2.问题

单机存在的问题:

单机也会存在强一致性问题,比如C1,C2都向服务器端的某个值发起更改共享变量A的操作且两者的目标
值不一样,C3,C4都请求读,此时C3和C4读取到的数据是一样的吗?

另外,单机的容错能力很差,如果它crash或者磁盘坏了,那就什么都不剩了.所以在现实生活中的分布式
系统我们实际上都会构建一个复制系统,但是当我们有副本的时候,这又引起新的问题.
这里有一个非常糟糕的关于副本的设计.这里提出这个糟糕的方案是为了让我们知道在GFS中也会有这个.

Bad Replication Design
假设我们有两台server,每台都有一份完整的数据副本,然后在磁盘上,它们都有这样一个key-value表格.
直观上,我们当然想要这两张表完全一致,所以当有一台server失败了,我们可以读写另一台服务器,这就
意味着每一个写操作都必须在所有的server处理,另外read只能在单台server处理,否则就无法容错了.
因为如果read必须同时和两台服务器打交道,就无法在失去其中一台服务器的情况下幸免,所以问题就来了,
如果客户端C1和C2想要同时执行这些写操作,假设C1和C2都分别想将SHARED_VALUE这个变量更新成1和2,
它们分别向两台服务器都发送写请求,于是问题出现了,C1和C2的请求完成后,SHARED_VALUE的值变成了多少?

这里我们没有做任何事情来保障两台服务器以相同的顺序处理这两个请求.这是一个非常糟糕的设计.
如果S1先执行C1的请求再处理C2的请求,但S2先执行C2的请求再执行C1的请求,就会导致S1和S2上
SHARED_VALUE值得不一致性.如果此时C3和C4都来请求同一个变量,假设它们的请求落到不同的服务
器,分别落到S1和S2上,那么请求的数据就不一致了.

当然,这个问题是可以修复的,修复这个问题的关键在于S1和S2之间需要进行更多的通信,某些地方也会变得
更加地复杂,因为想获得强一致性,不可避免地需要更多的开销.有大量的方案可以获得更好的一致性,
也有大量的方案可以获得让人感觉一定程度上还可接受的一致性.


没有同步措施,就是一个disastrous model.


一些关于GFS的思考,如何修复这些问题,他们做的挺好了,但是还不够完美.





3.关于GFS的经典论文的基本论述

GFS是在2003年提出的,已经有很长的时间了,那时候的web发展到相当规模了,人们也在建立大型网站,
此外,分布式系统领域也有了几十年的研究,人们至少在学术界知道如何去构建各种类型的高度并行化的
且具备容错性的系统,但是学术上的点子很少有能应用在工业上.但是自从这篇论文发表以后,像谷歌这
样的大型万丈才开始真正建立严格意义上的分布式系统.这件事让人热血沸腾,当然也包括我,作为学术界
的一份子,我切实地体会到了所有想法在工业界的实现.在谷歌,有海量的数据,这些数据躲到单个磁盘根本
无法存储,就比如说整个互联网抓取的网页副本,或者在这篇文章后边一点有个巨大的Youtube视频,他们
就会有一份比如中间文件的东西用来建立索引用于搜索,他们的web服务器显然会有大量的日志文件,以便
供给未来分析的时候使用.他们有大量的数据集,并且使用大量的磁盘来存储它们,借助MapReduce这样的
工具可以快速地处理这些数据,因此它们需要能够高度并行化得访问海量数据,所以它们的目标是:
这个存储系需要大容量,速度快,它们还需要一个文件系统,从某种意义上来说,这个文件系统必须是全球的
(覆盖在这个数据中心上),各种不同的应用程序都能从中读取数据,有一种建立大型存储系统的方法,假如
你有一些特殊的应用程序或者采集程序,你可以编写一个专门的存储系统,专门适应这些特别的应用程序.
如果你隔壁办公室的人也需要用到大型存储,他们就得自己去编写自己的村塾系统,而无法复用你的程序.
你要是有一个通用的全球的可用的存储系统,这就意味着我存储大量的从网页中抓取的数据,并且你也想要
看我抓取的网页,因为我们在一个沙盒(sandbox)里折腾且使用了相同的存储系统,只要访问控制允许的化,
你就可以读取我存的文件,所以就有了构建文件系统的想法.任何在谷歌的人都可以给任何文件命名或者读取
它,其目的就是为了共享.

为了得到大容量,速度快的特性,它们需要把数据进行分割,每个文件都将自动
地被GFS分割到许多server上.只要有许多客户端大量读取文件,这样读写操作将会自动变快(并行化读
写),你就能获得更高的吞吐量,而且能够让单个文件比单个磁盘还大,因为我们构建的东西在数百台服务
上,所以我们希望这些服务器能够自动地从错误中恢复.数百台机器中,每时每刻都有机器发生故障,这
时候必须有一个人跑到机器那里去对着服务器做一些事情,比如说:重启它或者迁移数据或者什么别的也
好.你肯定不愿意用这样的服务吧,如果他不能自我修复的话.还有一些并非是目标,比如说GFS被设计成
只在单数据中心上运行,我们并不会去讨论如何把副本放到世界各地,单个GFS只会安装在一个大机房里
面,只会运行在一个数据中心上.让这样的系统能够在副本彼此相距甚远的情况下工作,是一个有价值的
目标,但是这还是挺难的.这个单数据中心并不是面向客户的服务,GFS是Google工程师开发的内部使用
的工具,所以它们不会出售这一套程序,而且它是为大型顺序文件读写以多种方式定制的,有一个完全不同
的领域,比如说有专门为小块数据优化存储的存储系统,比如说银行余额需要有一个数据库,它可以读写
和更新100字节以内存有人们银行余额的记录,但GFS可不是这样的系统,它就是为了处理大文件,比如
GB,TB大小的文件,它只处理大文件的顺序访问而不是随机访问.它没有花费过多的经历让延迟变得更低.
而是把重点放在巨大的吞吐量上,比如兆字节的操作.

这篇论文于2003年发表在SOSP上,一个顶级系统学术会议,通常在这样的会议上,论文标准是需要很多
创新研究,这些创新在课堂上一定不会有的,这篇论文中的电子在那个时候并不特别新颖,像分布式,分
片(sharding),容错这些等,你能很好地理解并实现它们,但是这篇论文描述的系统是建立在成百上千
台机器上的,数量远远超过了以往学术界建立的系统所使用的机器.事实上,它被用于工业界,并折射
现实世界的经验,比如对已经部署的系统,什么不该做,什么该做,什么样的开销是最有效率的,这些
都是非常有价值的.这篇论文提出了一个相当异端的观点:认为存储系统具有弱一致性是可以接受
的,在那时候,学术界的观念认为存储系统就应该有良好的行为,像这里这个糟糕的复制系统,它返回
了错误的数据,它为什么要这么做而不是构建一个能够返回正确数据的系统呢?在这篇论文里,它
确实没有保证返回的正确的数据,它的目的是获取更好的性能,最后一件事就是这篇论文里使用的这个
单个master,在学术的论文里,你可能会有自动容错的副本用来恢复master,候诊区有许多的master进行
分工,但是在这篇论文里,它得以侥幸地只使用一个master并且能够工作地很好.




GFS:
Big
Fast
Global
Sharding
Automatic Recovery

Single data center
Internal Use
Big Squenctial Access

4.关于对错误数据的忍耐性

学生可能提出一个说网上投票数据是否准确的问题,老师给出以下说明:
很讽刺的是,谁关系web页面上投票的数量是否准确呢?又或者有别的什么错误呢?如果你在搜索引擎上
执行搜索,可能会有两万条数据但是有一条搜索结果丢失了这样的事情,这些结果的排序可能也不正确,
所以在这类系统中,它对错误数据的容错能力不需要向银行那么高,但这并不说明所有的web网站都可以
是错误的,比如你收了别人的钱,给别人打广告,你最好还是得保证数字的正确性,但不一定就是这样的哈.
另外GFS中有一些提供数据的方法,可以在应用程序中进行补偿.比如论文中介绍的应用程序应当把数据
及其校验和一起使用,并且要清晰地标记记录的边界,所以应用程序就能从GFS中恢复数据,提供服务可能
并不需要强烈保证数据的正确性.

5.GFS架构与设计

GFS的一般结构是由很多个客户端,还有一个master,尽管存在master的多个副本,在这里master保存有
从文件名到数据存储位置的映射,实际上是有两张表,然后还有许多块服务器(chunk server),可能会有
上百个,每一个可能都会有一两块磁盘,这里的master用来管理命名和追踪chunk的位置,chunk server
会存储实际的数据,这看起来是设计中最好的地方了.这两个地方完全彼此隔离起来,并能使用独立的
特性进行独立设计.master知道所有的文件,并且能够追踪这些chunk,chunk标识符,chunk中包含文件的
连续片段,每个chunk的大小是64M,如果我有1GB的文件,master就会知道这个文件的第一个chunk保存
在哪里,第二个chunk又保存在哪里.如果我想要读取这个文件的任意一部分,就需要询问这个chunk在
哪个server上,然后就去找那个server读取chunk,粗略来说就是这样.


说的更细一些,我们需要证明我们所讨论的关于系统如何保持一致性以及如何处理错误的问题,我们需要
了解master实际存储的是什么这些细节.


Master data中我们需要关注的是两张表:
1.一个是filename到chunkID(或中chunk handle)数组的映射,
  file name----->array of chunck handles(NV)
这张表是为了告诉你在哪里可以找到数据,或者这些chunck是什么,但仅仅拿着这样依噶chunkID你还是
没有办法做其他事情,但是master碰巧还有第二张表,它记录了每个chunckID到一些数据之间的映射,
其中一项是chunck server的列表,这个chunck server保存数据的副本,每个chunk被存储在多个chunk 
server上,所以这应该写上chunk server列表,即第二个映射表是:
2.handleID------>list of chunk servers(V),
                 version(# NV),
                 primary(V),
                 lease expiration.
LOG,checkpoint---DISK
 
每一个chunk都有一个当前版本号,所以master还得记住每
个chunk的版本号,对于每个chunk的所有写操作,都必须在chunk primary(主chunk)上序列化,这个primary
是所有副本之一,所以master记住了哪个是chunk server是primary,primary只能在某个租约时间内才能
担任primary,所以master还应该记住这个租约的过期时间.这些东西全部存储在RAMM(内存)中.

如果master宕机了,这些东西就全部丢失了,为了能够重启master并且不丢失文件系统相关的信息.
master会把这些数据存到磁盘上,像内存一里一样,读数据的时候中从内存里面读,但是写数据,至少这
部分数据也要写入磁盘,所以这种管理方式要求所有的master在磁盘上都有一个log,任何时候有数据
变更就会在磁盘上的之日追加一个条目,并且定期创建checkpoint.有些东西必须报错到磁盘上,有一些
不用,我猜测是这样.当然chunk handle数组肯定要保存到磁盘上,所以会给它打上NV(non-vilatile)
非易失的标签,表示它会被写入磁盘.list of chunk servers不会被保存到磁盘上,因为master重启
后会和所有chunk server通信,并询问它们保存了哪些chunk.所以我觉得它不用写入磁盘.version要不
要写入磁盘要求了解系统如何工作,不过我偏向于它会写入磁盘(NV),稍后我们讨论系统如何工作的时候
再来讨论这个问题.primary标识不用写入到磁盘,所以应该是volatile,因为master在重启之后就会忘记
谁是primary,对于一个chunk来说,只需要简单地等待60秒钟的租约期过了,然后它便知道对于这个chunk
来说确实没有primary在工作,这时候它可以安全地指定一个不同的primary,类似的,租约过期时间
也是volite,任何文件达到64MB边界的时候需要创建一个新chunk,又或者是当新的primary被指派
导致版本号改变的时候,master都必须首先追加一点记录到日志中,内容大概就是:我刚刚添加了个
chunk到这个文件里或者说我刚修改了一下版本号,所以每一次修改这些东西都需要写入磁盘.这篇
论文并没有讨论这么多细节,但是我们需要知道master修改这些东西总会存在一些速率限制,因为你
每秒能够写磁盘的次数就那么多,使用日志而不是用数据库的原因是你应该了解过b-tree或者是hash
表,使用日志是因为追加日志非常高效,你有一对最近的日志记录需要被添加,在一次磁盘旋转之后
把他们写入磁盘的某个位置,这个位置包含了日志文件的结尾EOF,然而如果使用b-tree来表达这些数
据的话,那就还得去磁盘上去寻找一个随机位置读写一点数据,所以日志会让这种写操作快些,为了
把这些操作反射到磁盘上.然而如果master宕机就必须重建它的状态,你可能并不想从日志文件从头
开始读取重建,因为server第一次安装启动的而时间开始距离今年可能有数年,所以master有时候会
额外地创建一份它完整状态的快照到磁盘,这可能会花费几十秒,比如一份中的时间,当它重启的时
候,它只需要回到最近的checkpoint的位置,然后重演从checkpoint之后的日志就可以了.

6.读操作

读操作的需求是读取特定文件的某个特定偏移量上的一定大小的数据:
(1)把文件名和偏移量发给master;
(2)master根据文件名从文件列表中找出文件对应的chunk handle和server列表等信息给客户端(cache);
(3)客户端向chunk server发送chunk handle和server列表;
(4)chunk server将查询到的数据返回给客户端.

应用程序知道有一个文件名以及一个应用程序想从某个位置读取的偏移量,所以应用程序会把
文件名和偏移量发送给master,master从文件表里查询文件名,你知道每个chunk大小是64MB,
它可以已用偏移量除以64MB来查找是哪个chunk列表,再把包含这些数据的副本的chunck server
列表返回给客户端,所以第一步是把文件名和偏移量发给master,第二步master发送chunk handle
和server列表给客户端.
现在我们有一些选择,我可以插叙这个server列表中的任何一个,论文里说会尝试猜测
网络上(也可能是机架上)那个server距离它最近,然后发送读请求到那个副本上.客户端实际
上会缓存这些结果,如果它尝试着再次读取这个chunk的话,客户端可能从返回的chunk中
读取1M或者64KB的片段,客户端可能会读取同一个chunk中不同的位置连续的区域多次,所以
我们需要缓存给定的chunk锁对应的是哪个serever,对于相同的信息,它就不用一遍又一遍的去
访问master.客户端与chunk server之一进行通信,发送一个chunk handle以及一个偏移,
chunk server 存储这些chunk,每个chunk在硬盘上都是一个独立的linux文件,位于普通的
linux文件系统中,假定这些chunk文件就是通过handle来命名,所以chunk server需要做的
就是使用正确的名字来找到这个文件,找到整个chunk之后,读取客户端期望的字节范围数据,
最后chunk server将数据返回给客户端.


学生的问题:
如果读取的数据跨块了怎么办?

老师的回答:
我其实不知道具体的细节,印象中是如果引用程序想要读取超过64MN的数据或者仅仅是想要
读取2个字节但是却跨越了chunk的边界,应用程序所链接的依赖库,就是那个底层发送数据
到不同server的依赖库,它将会注意到这个读请求跨越了一个chunk边界,于是它会把这条
读请求分割成两个读请求发送给master,意思是你只和master通信1次却会得到两条结果,
但是从逻辑上将,请求master两次,接下来就会请求两个不同的chunk server.


学生的问题:
不晓得啥问题.

老师的回答:
至少在一开始对于给定的文件,客户端不知道需要哪个chunk...
但是它需要知道是哪个chunk server持有这个文件的第17个chunk,客户端需要和master通信
才知道这个信息..
我不确保到底是哪种方式来判断它是文件中的第17个chunk,但是master能够找到第17个chunk
的handler标识符,它只要查一下它的表就能够知道哪个那个chunk server持有这个chunk.

学生问题的问题:
不晓得,觉得大概是和分块相关的问题.
老师回答说:
客户端本身连接了GFS的lib库,lib能够注意到并且知道如何把读请求分割,然后把他们的返回
结果合并起来,所以lib会和master进行通信,master会告诉客户端chunk 7在这个server上,
chunk 8在那个server上,lib就能对chunk server说我要chunk 7的最后两个字节和chunk 8的
头两个字节,接下来把请求到的数据放到buffer里返回给应用层.
lib能在给定的chunk中找到应用程序想要的数据,应用程序只要想好文件名以及整个文件中的
偏移位置,master就能根据这些信息返回chunk信息.


学生的问题:
从那个chunk server读取数据很重要吗?
老师回答说:
可以说重要也可以说不重要,从概念上来说,它们都是副本,事实上你可能已经主要到了,之前我们
讨论过它们并不都是完全相同,应用程序应该是能够忍受这样的情况的,但是事实上是依赖于你读
取的chunk server副本,你得到的数据会有稍稍不同.论文中说从相同的机架上或者是从同一交换
机后的server读取数据.


OK,读数据就是这个过程.


 

7.写操作

有一些函数调用,一些调用的库可以请求gfs,客户端lib库请求会说,看,我这儿有一个文件明以及一段
我想写的字节数据,这个buffer,我请求你能帮我写入.master的一部分试图弄清楚client端应该和哪些
服务器通信,所以当我们最终找到最新的副本的时候,up-to-date的意思就是一个副本中的chunk版本等于
master服务器知道的最新版本号,因为这些版本号是master服务器分发的,所以master能记住它们,对于
这个特定的chunk,仅当chunk服务器的版本号为17的时候它才是最新的,这就是为什么它必须是非易失性
需要存储在磁盘上的原因.如果版本号在奔溃中丢失,并且chunk服务器持有过时chunk副本,master就无法
这个chunk server上的chunk副本是上周的还是最新的,这就是为什么master服务器要把版本号记录在
磁盘上的原因.


1.客户端请求master告诉master我想要追加某个文件的内容.请告诉我需要与哪些服务器对话.
master的一部分试图弄清楚client端应该和哪些服务器通信



问题:(43:39)
没听清
老师的回答:
如果你正在与所有的chunk服务器通信,结论是如果master重启,master服务器无论如何都要与chunk
服务器进行通信来确定哪个chunk服务器持有哪个chunk,因为master不记得这些信息了,你可能会想,
只要找到版本最大的那个就行,你只需要与chunk服务器通信,找出他们拥有哪些chunk和版本,从所有
响应中找到version最大值,如果所有持有chunk的chunk服务器都响应,这种做法没有问题,但风险是
当master重启的时候也许有一些chunk服务器离线或者断开连接或者是别的什么原因导致它不响应master,
导致master拿到的都是持有前几周的旧副本的chunk server的响应,而具有最新副本的chunk服务器还
没有完成重启甚至离线,这样就会有问题的.我们知道master会定期和chunk server进行对话并且询问
它们拥有哪些块拥有什么版本,如果服务器上所有的最新版本都失效或者丢失的话,master知道这个
chunk在寻找版本17,假设它找不到这样的包含版本17的chunk服务器,master要么先不回复等待,要么
告诉客户端,我无法回答,请稍后再试.就像建筑物中的电源故障,所有的服务器都崩溃了,我们正在
缓慢地重新启动,master可能先完成,一些chunk服务器可能已经启动好了,其他的服务器五分钟之后
才能完成启动,所以必须等待,并且永远会等待,因为你不想使用chunk的旧版本.

所以master需要整合具有最新版本的chunk服务器列表,master知道磁盘上存储的最新版本,正如你
指出的,每个chunk server以及每个chunk也记得它存储的chunk 的版本号,当chunk向master报告说
我有这个chunk,master服务器便可以忽略掉哪些与master知道的最新版本不匹配的chunck server.

8.强一致性分布式文件系统的设计思路

1.拥有检测重复的能力;
2.当primary让secondary做事的时候,在遇到困难的情况下也要真正把事情做好了,而不是仅仅报告错我
  不能完成这个任务;在强一致性系统中,如果secondary能够随意终止primary而没有付出任何代价是不行
  的,secondary必须接受并执行请求.如果secondary磁盘有某种永久性损坏,例如错误地拔出了磁盘,你
  需要一种机制将secondary从系统中移除,这样primary就可以和剩下的secondary继续工作(那少了个
  secondary咋办?)

这两点GFS都没有做,至少是没有做对.这也意味着当primary要求secondary追加什么的时候,
secondary必须小心不要将数据暴露给请求者,直到primary确信所有secondary都能够执行
追加操作为止.因此你可能需要把写操作分成多个阶段:
第一阶段:
primary请求secondary,问问,我想让你执行此操作,你能执行此操作吗?但此时secondary并不是
真正地执行此操作,如果所有secondary都回答说能够进行操作,这时进入第二阶段;
第二阶段:
primary说,好的,去做吧,这样每个secondary都会执行所承诺的动作.

这是现实世界中很多强一致性的系统的工作方式,这种技巧称为两阶段提交.


另一个问题:
如果primary崩溃,但是已经有最后一组primary发给secondary的操作开始了,而primary在确定所有
secondary拿到拷贝之前就崩溃了,所以如果primary奔溃,其中的一个secondary将会接替新的primary,
但是在那时新的primary和其余的secondary在最后的几次操作中可能有所不同,因为也许其中一些在primary服务器奔溃之前没有收到消息,因此新的primary开始是必须与secondary显示地重新同步,以
确保他们的操作历史的最后几个步骤是一样的.

最后,为了解决这个问题.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值