Frangipani:6.824课堂笔记

Frangipani是一个分布式文件系统,其核心关注点在于缓存一致性、分布式事务和崩溃恢复机制。工作站上的文件系统数据结构在本地缓存,并通过lockserver和petal集群管理一致性。缓存一致性通过锁协议实现,事务原子性通过分布式事务保证,崩溃恢复利用预写式日志确保未完成事务的恢复。然而,由于缺乏安全措施和对大规模分布式环境的支持,Frangipani主要适用于小规模、高信任度的环境。
摘要由CSDN通过智能技术生成

frangipani这篇paper,我们需要学习的主要是其中的缓存一致性、分布式事务、分布式崩溃恢复的机制设计。

frangipani一般就是作为一个工作站的内核的一个模块,当这个工作站上的程序进行文件系统的系统调用时就会调用内核模块中的frangipani这个模块。

frangipani模块中保存了文件系统的数据结构,比如:文件内容、inode、目录等,以及一些信息描述了哪些inode和block还没被使用。工作站中所有的文件数据最终都是保存到petal的虚拟磁盘上,petal是一组机器专门存放frangipani工作站的数据的。当工作站需要读取文件的时候,就向petal发起rpc请求即可。petal可以想象成一个由多个工作站共享使用的磁盘即可,这个磁盘和工作站之间通过网络来通信,

frangipani的缺陷以及特殊应用场景


frangipani提供了这么一个特别的网络文件系统,这个文件系统可以由一个组织中的所有人共享使用,上面不仅可以保存每个人的home目录,还可以保存他们共享的项目文件。在这些共享的文件中,任何一个人修改了这个文件,其余人也能立刻看到这个文件的变动。

这种很强的共享能力,可以使得我在任何一台电脑都共享一个文件系统,我更换任何一台电脑都可以轻松访问到任何文件,无需考虑文件的转移问题,以及环境配置问题。

这种文件系统对于那种小的用户群体其实很好用,在那些小群体中大家都彼此信任,所有的电脑也都是可信的。简而言之就是没有任何安全的设防,因此frangipani的一个问题就是在此,它无法用于公开的环境中,它没有任何安全上的设防。

此外,frangipani所提供的环境是为小团体使用的,人们坐在工作站前,人们通过它来共享文件,但是它并没有真正运用到分布式存储中。当前分布式存储是要运用于大型数据中心、大型网站、大型数据计算中。在这些领域中,文件系统接口也不是非常有用。

比如人们喜欢在大型网站中使用事务,在很小的几条数据上使用事务,人们会将这种类型的数据存放到数据库中,而非文件系统中。

在大型数据计算中,比如MapReduce,frangipani的侧重于缓存一致性和锁上面,这对于大容量的数据的读取和写入并没有特别的用处,反而在读取大容量的数据,你使用缓存反而会适得其反,降低性能。

因此frangipani中的很多东西其实已经过时了,它主要用在上面提及的那个特殊场景,在当前的生产环境下,人们在设计新的系统的时候基本不会考虑使用它。

frangipani的设计


缓存机制

工作站从petal从读取了文件后,会在本地缓存一份,后续就是对本地的缓存进行操作。

write-back caching机制,每个工作站的文件修改操作都是在本地进行的,这些操作都可以十分快速的完成,这对于性能的提升十分巨大。在本地的动作无需通过RPC向服务器发起请求来做到。

当然,在本地进行文件的任何操作并保存在本地的缓存中,这意味着本地的frangipani模块中必然支持完整的文件操作功能,这些复杂文件操作逻辑功能都封装到了客户端本地的frangipani模块中,这是一种去中心化的体现。它将复杂性和大多数的CPU时间都是花在客户端上,因此,你给这个分布式的系统中添加新的客户端进来时,并不会给中心的petal存储系统带来的很多的负担,因为绝大数的负担都是由客户端自身承担。petal无须理解任何关于文件系统方面的东西,它就是一个简单的数据块服务器。

这就确保了frangipani能够使得这个分布式文件系统具有很好的扩展性。

挑战

缓存机制显然需要考虑的是一致性的问题。用户A发起了文件的修改,但是修改操作都是在本地缓存进行,如何让其他用户能够看到最新的文件。

因此为了确保这种强共享性,这种强一致性必须也要保证,那么就需要实现缓存一致性。

此外,如果两个用户同时在一个目录下进行文件的修改操作,如何确保这两个操作不会冲突。如何处理并发的请求,并让结果变得合理是十分重要的。我们需要避免其中一个操作干扰另个操作的结果。那么就是需要实现原子性。

此外,在分布式系统中必须考虑的一个问题就是服务器的崩溃恢复能力。如何避免工作站的崩溃导致整个分布式文件系统受到影响,使得其余的工作站也受到影响,其余工作站应该是看到一些合理的东西,而非由于崩溃留下一堆损坏的文件系统数据结构。那么就需要设计崩溃恢复能力。

因此Frangipani需要重点关注的就是以上三个挑战:1、缓存一致性;2、原子性;3、崩溃恢复能力

当然Petal可能也会崩溃,但是它自身内置了一套独立的容错机制,类似链式复制的那类分布式系统。

缓存一致性

这里就是让每个用户看到的文件系统上的数据都是最新的。但是就是需要具备线性一致性的缓存。

缓存一致性我们或许在多核处理器中就已经学习到了,但是这里的缓存一致性所使用的协议不大一样。

frangipani的缓存一致性协议是通过锁来实现的。

frangipani的分布式文件系统中,不仅只有工作站、Petal存储机器集群,还有一个lock server。

lock server有一张表,上面记录了各种锁,每个文件都应该对应一个锁,当一个工作站需要对某个文件进行操作时,就需要对这个文件对应的锁进行获取,这里将这个锁简化为了独占锁。其实上,frangipani中的锁应该分为读锁和写锁,其实就是操作系统中对应的那类锁,允许多个用户进行读,允许一个用户进行写的锁。

不仅在lock server中有一张锁表,在每个工作站本地也有一张锁表记录了文件,对应的锁的状态,还有就是文件的修改内容。

工作站在使用一个文件结束后,并不会马上向lock server申请释放锁,而是将这个文件的锁在本地锁表中的状态由busy转变为idle空闲状态。因为,基本上,工作站在对一个文件修改后,后续还要需要用到这个文件,这就可以避免后续再需要用这个文件再去重新申请锁。因此,每个工作站最后可能会堆积了大量的idle锁,后续碰到REVOKE的释放锁的请求,便会释放锁。

此外,还有一个情况我们需要注意,工作站不修改完文件,将锁的状态变为idle,但是始终不将修改内容写回petal并释放锁,如果工作站发生了崩溃,大量的修改内容都将会丢失。为了防止这种事情发生,需要每30秒将这些工作站中的脏的缓存写回到petal中。

一个工作站在缓存一个文件的前提是必须获取到这个文件的锁。因此,这个锁机制就确保了工作站在获取到锁后,再去查看文件必然是最新状态的。

整个流程简单来说就是:1、工作站向lock server申请锁,后续向petal读取文件并缓存;2、工作站拥有这个文件锁,对该文件在本地缓存中进行了修改;3、释放锁前将本地缓存中的修改内容写回petal;4、释放锁,并在本地的锁表中移除该条目

当一个工作站向lock server申请一个文件的锁时,发现被另个工作站持有,那么lock server就会发送消息给持有锁的工作站,催它释放锁。如果工作站发现该锁的状态是idle,并且缓存数据是脏的,那么就立刻将修改内容写回petal然后再释放锁。

以上的这些流程涉及到的缓存一致性协议,总共需要4类消息

1、REQUEST:这个是工作站向lock server,发起锁的申请消息

2、GRANT:这个lock server向工作站,回复申请通过

3、REVOKE:工作站向持有锁的工作站催它释放锁

4、RELEASE:工作站向lock server发送释放锁的消息

以上就是frangipani的简化版的一致性协议,实际上的一致性化协议中涉及到的是写操作的独占锁和用户只读访问的共享锁。

缓存一致性协议的套路,简而言之就是,为了确保每个人都能去读到最新的数据,它会让前一个持有锁的人将最新数据先写回petal,后续别人才能读取数据。这确保了写回和读之间的严格顺序。

原子性

文件的创建操作往往涉及了诸多步骤,像分配一个新的inode,分配空间等。需要去更新很多内容,但是其中的中间步骤应该不对外披露,这些中间步骤应该具有原子性。

因此,frangipani实现了数据库中的事务这个概念。事务也是由锁驱动的。在这个分布式的文件系统中,这其实就是一个分布式事务,为了实现这个原子性,那么就是需要在一个工作站完成其操作之前,其余工作站无法查看,无法看到中间步骤。

一个工作站在完成一个事务前,需要先获取这个事务涉及到的所有文件的锁,等修改结束后,将修改内容写回Petal再释放锁。这个整体流程十分简单,通过持有事务流程所需的全部锁,十分轻而易举地得到了事务的原子性。

这里锁的使用在两个近乎相悖的目的上发挥了作用。首先通过锁来实现了缓存一致性,确保刚写入的最新数据能够立刻被其他人看到。而后又通过锁来实现原子性,确保只有一个工作站结束了它的操作,其他人才能看到写入的内容。

崩溃恢复

一个工作站可能在持有一个文件的锁,并进行操作的过程中发生崩溃。大概率这个工作站正在进行的操作是没有执行结束的,并且这个锁也被这个工作站始终持有,其他工作站无法获取涉及的文件的锁。

frangipani通过预写式日志 write-ahead logging来实现恢复崩溃的事务。一个工作站要将一个事务的执行结果写回到petal前,需要将这个事务的操作日志先发送给petal,只有这个事务的完整操作日志被petal接受后,工作站才可以进行后续的写回操作。

在frangipani中,这个预写式日志有点特殊,在petal中的日志数量等同于工作站的数量。每个工作站都有一个对应的日志,这个日志和这个工作站的执行事务紧密相关,这个日志并非是存储在工作站本地,而是存储在远程的petal上,这确保了这个事务在工作站崩溃后,也可以借助petal上的日志来继续完成这个事务的执行。

每个日志都由大量的日志条目组成,每个条目中的信息应该有版本号version、数据块号block(即该操作涉及的数据块号)、修改的内容Data。并且每个日志条目都应该有一个递增的序号,这个序号代表了日志条目的先后顺序。

当一个工作站接受到了REVOKE的消息后,要求释放文件Z的锁,便会把最后一条和该Z文件有关的日志条目以及之前的所有日志条目发送给petal(只是把和文件z相关的日志写回petal即可,如果将整个日志完整的写回petal,那么就开销就太大了,因此这么操作是优化性能的结果,剩余部分延后写回到petal),写回日志后就是把脏数据发送给petal,后续即可释放锁。

工作站崩溃的时间点

工作站崩溃有几种可能的时间点。

1、在发送日志给petal前就崩溃了,那么就当做什么修改都没有发生。

2、在发送日志成功后,在发送执行缓存结果给petal时崩溃了,那么就可通过petal中的日志来继续完成崩溃的事务。

3、在缓存发送结束后崩溃,那么结果也已经成功写回了,锁也释放了,崩溃也不会有任何影响。

着重介绍第二种情况,在工作站将任何修改过的文件或者目录写回petal时发生了崩溃,此时必然是已经将整个日志发送给了petal。此时这个锁一直被崩溃的工作站持有,当lock server发送给工作站REVOKE消息时,迟迟无法得到回应,当经过一段时间,超时后,lock server将自动判定该工作站崩溃断连,这个其实就是lease租赁的设定,超过了规定时间就判定为崩溃。lock server会告知其他工作站这个崩溃的消息,并请求它们去读取崩溃的工作站的日志并通过日志来恢复崩溃的事务。当恢复完成后,告知lock server,lock server便会自动将这个锁释放。

恢复的过程其实就是日志条目的序号顺序读取每一条日志,并根据日志中的block号来定位修改位置,以及data来确定修改的内容。注意,petal中的这条日志条目如果不是完整的,那么这条日志条目指定的修改操作也不会执行。这其中也应该隐含了checksum之类的。应该是执行一个完整的操作而非一半的操作。

关键问题:工作站在发送缓存过程中,发送了多少给petal,还有哪些日志条目对应的缓存还没发送,另一说法就是应该继续执行哪些日志条目,需要避免重复执行修改操作

此时,日志条目中的version版本号发挥了作用,在petal中的每个文件都对应一个version版本号,每个日志条目中的版本号等于当时操作的的文件的版本号加1。如果这个日志条目尚未执行,那么对应的文件的版本号应该等于次日志条目版本号减1。因此,如果文件的版本号大于等于日志条目中的版本号,表明这个日志对应的操作已经执行过了。

有可能工作站崩溃的时候,它已经释放了所有文件的锁,即这种情况,其他工作站去检查它的日志会发现它的所有日志条目对应的文件都没问题,无需任何操作。但是,有可能对应文件的锁可能正好被其他工作站持有,此时日志检查流程无需考虑锁的问题,直接继续查看文件情况即可。

因为,如果文件版本号有问题,需要执行日志条目,那么表明这个文件的锁必然被崩溃的工作站持有,那么此时检查文件就不需要获取锁。如果文件版本号没有问题,那么就无需任何操作,即使锁被其他工作站持有也无所谓。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值