The Google File System 翻译和理解( 九 )


在快照操作后,我们以下面的方式来执行复制写入:

  1. 客户端第一次想要向块 C 中写入数据前,它将向 Master 发送一个请求来查找当前的租约持有者 。
  2. Master 注意到块 C 的引用计数大于 1,它将推迟回复客户端的请求,并选择一个新的块句柄 C’,然后通知每个拥有块 C 副本的块服务器,创建一个新的块 C’ 。通过在同一个块服务器上创建一个新的块,我们能确保这个拷贝是本地的,不需要通过网络进行的(我们的磁盘速度是100MB以太网链路的3倍) 。
  3. 从这点上看,请求的处理不会与其它的块处理有差别:主节点将新块 C’ 的租约授予其中一个副本,并回复客户端,客户端能够进行一般的写操作,并不知道这个块是从一个已存在的块上创建出来的 。
4. Master操作Master 完成了以下这些工作:
  1. 执行所有的命名空间操作 。
  2. 管理整个系统内的所有块的副本 。
    • 决定块的存储位置 。
    • 协调系统范围内的各种行为以保障块能够有足够的副本 。
    • 均衡所有块服务器的负载 。
    • 以及回收不再使用的存储空间 。
4.1 命名空间管理与锁许多 Master 操作会占用较长时间:例如,一个快照操作必须撤销所有进行快照的块所在块服务器的租约,我们不想推迟正在进行的其它 Master 操作,因此,我们允许多个操作同时进行,并使用命名空间区域的锁来确保这些操作按适当的顺序执行 。
不像许多传统的文件系统那样,GFS 没有一个目录数据结构来列出这个目录下的所有文件,也不支持对文件和目录的别名操作(如,Unix术语中的硬链接或符号链接) 。GFS 的命名空间逻辑上表现为一个将全路径名映射为元数据的查询表 。利用前缀压缩,这个表能够高效的存储在内存中 。每个命名空间树中的节点(无论是一个绝对文件名还是一个绝对目录名)都有一个与之关联的读写锁 。
每个主节点操作在执行前都先获得一系列的锁,通常,如果它涉及到/d1/d2/.../dn/leaf,它将:
  • 获得目录名的读锁/d1,/d1/d2,...,/d1/d2/.../dn
  • 获取完全文件名/d1/d2/.../dn/leaf的一个读锁或写锁 。
注意,这里的 leaf 根据其操作,可能是一个文件,也可能是一个目录 。
我们现在举例说明在/home/user构造快照成/save/user时,这个锁机制如何防止另一个 Master 任务创建一个文件/home/user/foo
  • 快照操作获得了/home/save上的读锁,以及/home/user/save/user上的写锁 。
  • 而文件创建操作则获得了/home/home/user上的读锁,以及/home/user/foo上的写锁 。因为它们都试图获得/home/user上的锁而造成冲突,所以这两个操作将适当的进行排序 。
文件创建不需要获取它的父目录的写锁,因为这里没有“目录”或类似 inode 等用来防止被修改的数据结构 。文件名上的读锁足以防止父目录被删除 。
这种锁机制的一个很好的性质是它允许对同一个目录下的并发操作 。比如,在一个相同目录下,多个文件创建操作可以同时进行:每个操作获取目录上的读锁,以及其文件名上的写锁 。目录名上的读锁足以防止目录被删除、重命名或进行快照 。文件名上的写锁可以使使用同一名字创建文件的两次操作顺序执行 。
因为命名空间可能包含很多节点,所以读写锁对象采用惰性分配策略,一旦不再使用则被删除 。同样,锁需要按一个相同的顺序被获取来防止死锁:他们先对命名空间进行排序,并在同一级别按字典序排序 。

经验总结扩展阅读