|
分布式文件系统的主要功能有两个:一个是存储文档、图像、视频之类的Blob
, ` ` x0 z3 k Z3 y3 X0 I类型数据;另外一个是作为分布式表格系统的持久化层。
- @, G, T* U. M' x3 ? L" k分布式文件系统中最为著名的莫过于Google File System(GFS),它构建在廉价' y6 O' x' L9 R- f; f- _7 C
的普通PC服务器之上,支持自动容错。GFS内部将大文件划分为大小约为64MB的数
/ f* o% a+ r/ e" [据块(chunk),并通过主控服务器(Master)实现元数据管理、副本管理、自动负) {) w' |% f5 k
载均衡等操作。其他文件系统,例如Taobao File System(TFS)、Facebook Haystack/ G! r0 u" e! T/ |- I1 B- u8 x
或多或少借鉴了GFS的思路,架构上都比较相近。
$ P% y; E7 |( x8 H% W本章首先重点介绍GFS的内部实现机制,接着介绍TFS和Face book Haystack的内( T$ t9 x. a: V9 u9 q6 x
部实现。最后,本章还会简单介绍内容分发网络(Content Delivery Network,CDN)技& e/ W+ s c( q( f% F. b) x
术,这种技术能够将图像、视频之类的数据缓存在离用户“最近”的网络节点上,从
% _$ r M: d8 z3 ]( _而降低访问延时,节省带宽。
+ r$ Q! t3 c/ l, P4 a4.1 Google文件系统
% g; d* O$ ~, h. r( xGoogle文件系统(GFS)是构建在廉价服务器之上的大型分布式系统。它将服务 ]7 @5 M( J) g8 W
器故障视为正常现象,通过软件的方式自动容错,在保证系统可靠性和可用性的同
" m# o" \; G. S( k: u9 U, K( K时,大大降低系统的成本。) b/ O9 J' ]: i( Q+ t
GFS是Google分布式存储的基石,其他存储系统,如Google Bigtable、Google% i7 T* N) q2 P9 B
Megastore、Google Percolator均直接或者间接地构建在GFS之上。另外,Google大规" H" C8 |& a( x3 S
模批处理系统MapReduce也需要利用GFS作为海量数据的输入输出。
& I" f. t! G8 q( b4.1.1 系统架构& b, F' P( n9 j; \
如图4-1所示,GFS系统的节点可分为三种角色:GFS Master(主控服务器)、: e+ A0 u5 d& e5 E* O2 i2 C
GFS ChunkServer(CS,数据块服务器)以及GFS客户端。( C, K7 p# G% M7 f
图 4-1 GFS整体架构
. C) K% D" u9 B' }4 |$ y% }( z7 ^0 CGFS文件被划分为固定大小的数据块(chunk),由主服务器在创建时分配一个
* D( b- ^' z; u64位全局唯一的chunk句柄。CS以普通的Linux文件的形式将chunk存储在磁盘中。为9 N2 b; F! g4 G# y w! m% k: Q9 i
了保证可靠性,chunk在不同的机器中复制多份,默认为三份。
4 }- {6 J- [& c7 b主控服务器中维护了系统的元数据,包括文件及chunk命名空间、文件到chunk之
/ g) i- S Q6 ^& D- v! X间的映射、chunk位置信息。它也负责整个系统的全局控制,如chunk租约管理、垃圾
8 K' d, B5 @9 B, w回收无用chunk、chunk复制等。主控服务器会定期与CS通过心跳的方式交换信息。% B h( u+ X- G4 M, r0 {2 y3 C
客户端是GFS提供给应用程序的访问接口,它是一组专用接口,不遵循POSIX规0 ^' j3 K5 G* @: j& v1 C
范,以库文件的形式提供。客户端访问GFS时,首先访问主控服务器节点,获取与之
, w1 _. @3 _1 y3 h8 _* J进行交互的CS信息,然后直接访问这些CS,完成数据存取工作。8 s9 }8 M9 g+ ~3 c6 r# ?: j% d
需要注意的是,GFS中的客户端不缓存文件数据,只缓存主控服务器中获取的元
5 f$ |" b" ~: k6 s; {数据,这是由GFS的应用特点决定的。GFS最主要的应用有两个:MapReduce与. v5 D7 [- Q' s! S* e
Bigtable。对于MapReduce,GFS客户端使用方式为顺序读写,没有缓存文件数据的必% A# D8 C5 N4 y5 N- c
要;而Bigtable作为分布式表格系统,内部实现了一套缓存机制。另外,如何维护客
6 a3 e6 k2 v0 I7 I( Y, L d: h户端缓存与实际数据之间的一致性是一个极其复杂的问题。; f' w! @- ~- e% {
4.1.2 关键问题
% J# ?6 d# C- f( d4 x% M1.租约机制
/ Y# L7 Y5 S$ `, T# U$ c4 QGFS数据追加以记录为单位,每个记录的大小为几十KB到几MB不等,如果每次; S, I. @ i. r: |* o
记录追加都需要请求Master,那么Master显然会成为系统的性能瓶颈,因此,GFS系
) O5 {' I7 ~, i# l; G {: j统中通过租约(lease)机制将chunk写操作授权给ChunkServer。拥有租约授权的" N' r' K/ j4 {7 v& F
ChunkServe称为主ChunkServer,其他副本所在的ChunkServer称为备ChunkServer。租* o. n7 ^* P0 ?0 S3 u' s$ j1 }
约授权针对单个chunk,在租约有效期内,对该chunk的写操作都由主ChunkServer负+ N% }/ P8 Q/ Z9 _: `$ C
责,从而减轻Master的负载。一般来说,租约的有效期比较长,比如60秒,只要没有
& N* u) E3 |! E! w4 T* }# k出现异常,主ChunkServer可以不断向Master请求延长租约的有效期直到整个chunk写
4 \) C+ I# F! D0 Q" Y3 n* e满。. P% L. j: e5 ?% }* V Y
假设chunk A在GFS中保存了三个副本A1、A2、A3,其中,A1是主副本。如果副
( y8 a& x' |1 L. |本A2所在ChunkServer下线后又重新上线,并且在A2下线的过程中,副本A1和A3有9 T$ y% q3 g1 V& m5 A( y
更新,那么A2需要被Master当成垃圾回收掉。GFS通过对每个chunk维护一个版本号
3 ]+ V0 Q$ N0 u; K2 P6 q来解决,每次给chunk进行租约授权或者主ChunkServer重新延长租约有效期时,) o; I, m& \' j4 s9 k
Master会将chunk的版本号加1。A2下线的过程中,副本A1和A3有更新,说明主0 H ? Y" X3 ^. o5 f2 Q
ChunkServer向Master重新申请租约并增加了A1和A3的版本号,等到A2重新上线后,
4 ?1 j4 M7 r. ^' mMaster能够发现A2的版本号太低,从而将A2标记为可删除的chunk,Master的垃圾回收
) ~: y% m/ u/ ?; x. w' ^任务会定时检查,并通知ChunkServer将A2回收掉。
|2 l% N \1 L2 o& {, ]- Z3 {3 c. v2.一致性模型
}" ~- T) v! r/ Z& WGFS主要是为了追加(append)而不是改写(overwrite)而设计的。一方面是因
/ E* V5 e# n6 u/ @2 m' o为改写的需求比较少,或者可以通过追加来实现,比如可以只使用GFS的追加功能构& z; I: I6 k" s
建分布式表格系统Bigtable;另一方面是因为追加的一致性模型相比改写要更加简单% J' B1 k8 B* [" \# \
有效。考虑chunk A的三个副本A1、A2、A3,有一个改写操作修改了A1、A2但没有& r) X+ n! i U& W* d4 R# B7 A
修改A3,这样,落到副本A3的读操作可能读到不正确的数据;相应地,如果有一个2 P, z* U3 l8 L) p& T# f- y% A% {
追加操作往A1、A2上追加了一个记录,但是追加A3失败,那么即使读操作落到副本( {8 Z: w/ [/ T0 s
A3也只是读到过期而不是错误的数据。
: d% @' |$ V& W$ {2 O) w6 k5 x, ]我们只讨论追加的一致性。如果不发生异常,追加成功的记录在GFS的各个副本
, G2 B1 E1 t) C8 F5 F中是确定并且严格一致的;但是如果出现了异常,可能出现某些副本追加成功而某# @9 J; x/ C! S, n" T
些副本没有成功的情况,失败的副本可能会出现一些可识别的填充(padding)记
: I1 L' i! s# U; O0 U& X, c录。GFS客户端追加失败将重试,只要返回用户追加成功,说明在所有副本中都至少 h3 h5 G% I9 I
追加成功了一次。当然,可能出现记录在某些副本中被追加了多次,即重复记录;
$ g: r, t. z& L2 ^; ~' U! M也可能出现一些可识别的填充记录,应用层需要能够处理这些问题。
( o/ x6 B( O- [7 @另外,由于GFS支持多个客户端并发追加,多个客户端之间的顺序是无法保证% m4 t9 ~# f0 K, F" O+ A! _9 w
的,同一个客户端连续追加成功的多个记录也可能被打断,比如客户端先后追加成! A* w3 S: z/ W8 A! Z
功记录R1和R2,由于追加R1和R2这两条记录的过程不是原子的,中途可能被其他客* X7 u9 m4 h: K% ]% v
户端打断,那么GFS的chunk中记录的R1和R2可能不连续,中间夹杂着其他客户端追" L! N& M- r8 F4 i# L6 e
加的数据。
9 y# a+ f8 N7 j" Z- TGFS的这种一致性模型是追求性能导致的,这增加了应用程序开发的难度。对于
/ S8 J; V0 r2 v+ `0 u$ JMapReduce应用,由于其批处理特性,可以先将数据追加到一个临时文件,在临时文) F, w9 g) g8 @% F. D S
件中维护索引记录每个追加成功的记录的偏移,等到文件关闭时一次性将临时文件
! N* X& a2 _4 r* A改名为最终文件。对于上层的Bigtable,有两种处理方式,后面将会介绍。; K1 w, {$ `( r! ^# f/ c
3.追加流程
5 B, v$ p, X- Q$ F( p追加流程是GFS系统中最为复杂的地方,而且,高效支持记录追加对基于GFS实
' R% a; n" |# G; o2 ?9 N+ J现的分布式表格系统Bigtable是至关重要的。如图4-2所示,追加流程大致如下:7 P) I0 s3 T& E" E
图 4-2 GFS追加流程( x) V) q1 a# e) j }
1)客户端向Master请求chunk每个副本所在的ChunkServer,其中主ChunkServer持
0 x9 l: x$ a/ i有修改租约。如果没有ChunkServer持有租约,说明该chunk最近没有写操作,Master- h1 p0 f2 a# [; ~) L9 W' K
会发起一个任务,按照一定的策略将chunk的租约授权给其中一台ChunkServer。; V5 e- M8 c0 d, V/ K8 g
2)Master返回客户端主副本和备副本所在的ChunkServer的位置信息,客户端将9 K$ g! @: p$ ~! R
缓存这些信息供以后使用。如果不出现故障,客户端以后读写该chunk都不需要再次
/ q$ N4 \& e- @- Z: x. R6 h请求Master。9 I2 V) [& ~( d+ |0 [8 y, |- x# L
3)客户端将要追加的记录发送到每一个副本,每一个ChunkServer会在内部的
$ ^4 p0 [2 H4 g$ M; cLRU结构中缓存这些数据。GFS中采用数据流和控制流分离的方法,从而能够基于网9 {3 s2 [2 c% Q- b" R$ b- B8 T
络拓扑结构更好地调度数据流的传输。" ?7 j' I# @# A( E1 ^/ y
4)当所有副本都确认收到了数据,客户端发起一个写请求控制命令给主副本。( ^0 _- x. d9 D7 }& \7 _; H+ R
由于主副本可能收到多个客户端对同一个chunk的并发追加操作,主副本将确定这些
; i/ f" Q2 L5 x' W- ]操作的顺序并写入本地。
2 R7 M8 J. U. `& n. w" b* b5)主副本把写请求提交给所有的备副本。每一个备副本会根据主副本确定的顺* R9 P3 |$ {9 z: C1 ~) Z* y2 w$ I
序执行写操作。
$ m! d. {! x( B1 s6)备副本成功完成后应答主副本。2 Q+ S0 P5 [% Y) b! @1 r
7)主副本应答客户端,如果有副本发生错误,将出现主副本写成功但是某些备1 F7 v. L: m* w! U; ^
副本不成功的情况,客户端将重试。9 u7 B. m& S) m
GFS追加流程有两个特色:流水线及分离数据流与控制流。流水线操作用来减少
- C. V# ]5 z! o9 p F! X+ D) M7 s延时。当一个ChunkServer接收到一些数据,它就立即开始转发。由于采用全双工网$ S* }. n) W) ^9 u' T$ P9 m
络,立即发送数据并不会降低接收数据的速率。抛开网络阻塞,传输B个字节到R个. A% d; B" i% A n% Y5 a
副本的理想时间是B/T+RL,其中T是网络吞吐量,L是节点之间的延时。假设采用千7 a% r0 F" Z' { J. B
兆网络,L通常小于1ms,传输1MB数据到多个副本的时间小于80ms。分离数据流与; Z( t1 q/ y- z! x
控制流主要是为了优化数据传输,每一台机器都是把数据发送给网络拓扑图上“最
, j S; A& Q7 Z0 \8 H4 E近”的尚未收到数据的数据。举个例子,假设有三台ChunkServer:S1、S2和S3,S1与* q b2 q7 b0 K7 V, L, }8 ]! `
S3在同一个机架上,S2在另外一个机架上,客户端部署在机器S1上。如果数据先从
8 C6 ]/ I/ N( z/ hS1转发到S2,再从S2转发到S3,需要经历两次跨机架数据传输;相对地,按照GFS2 e! h5 S9 j2 @9 j4 v
中的策略,数据先发送到S1,接着从S1转发到S3,最后转发到S2,只需要一次跨机1 @7 O: Z# ?5 G$ E
架数据传输。
# P! V& U# h# v+ [3 F分离数据流与控制流的前提是每次追加的数据都比较大,比如MapReduce批处理
: M! n, r( ]) X2 X, ~ l% g系统,而且这种分离增加了追加流程的复杂度。如果采用传统的主备复制方法,追$ R V, W+ O; a$ P2 {
加流程会在一定程度上得到简化,如图4-3所示:! C( K9 @6 t2 w R0 B
图 4-3 GFS追加流程(数据流与控制流合并)
. ]% j1 m4 A; B. W$ y7 d1)同图4-2 GFS追加流程:客户端向Master请求chunk每个副本所在的$ g0 H4 h* E+ E4 Z% Y5 W) p
ChunkServer。3 F0 P) x, G; I' ]% F, V
2)同图4-2 GFS追加流程:Master返回客户端主副本和备副本所在ChunkServer的
% w L4 U0 c @' g: ~位置信息。
/ W# `& |: h; z: Y; G3)Client将待追加数据发送到主副本,主副本可能收到多个客户端的并发追加* O* |! k. u1 O/ i, F; g
请求,需要确定操作顺序,并写入本地。
* I9 ] y: S" n2 C, d* }4)主副本将数据通过流水线的方式转发给所有的备副本。3 V0 {% c7 V5 r) G; v
5)每个备副本收到待追加的记录数据后写入本地,所有副本都在本地写成功并8 w" {" P8 w; G+ x) t
且收到后一个副本的应答消息时向前一个副本回应,比如图4-3中备副本A需要等待- W* R. j2 ~- B Z j8 y* ]3 a
备副本B应答成功且本地写成功后才可以应答主副本。
, ^1 Y, ~- P/ g% X5 Z6)主副本应答客户端。如果客户端在超时时间之内没有收到主副本的应答,说6 t/ k" D/ W1 r3 O: @
明发生了错误,需要重试。4 \4 S. ?( h( `1 d3 Y
当然,实际的追加流程远远没有这么简单。追加的过程中可能出现主副本租约* o% Y9 a1 o, H( z u
过期而失去chunk修改操作的授权,以及主副本或者备副本所在的ChunkServer出现故
: }* r7 B1 V, ?$ H* x障,等等。由于篇幅有限,追加流程的异常处理留作读者思考。 _& u! ~# J; b- G4 s Q2 z
4.容错机制
3 w9 r* a' W/ q# o; u(1)Master容错( v' L1 o" J. P2 T3 x: _
Master容错与传统方法类似,通过操作日志加checkpoint的方式进行,并且有一
A* X0 ~+ }3 m, `: [# g; C3 e台称为"Shadow Master"的实时热备。: p: |7 @4 \8 ~& p
Master上保存了三种元数据信息:
. g& m, x d4 Z( M+ b●命名空间(Name Space),也就是整个文件系统的目录结构以及chunk基本信
/ Y+ S) P9 U0 p( U; [息;
/ d* j1 m) P, c6 E●文件到chunk之间的映射;
# w+ X5 a# U C5 Y% \% O3 ^●chunk副本的位置信息,每个chunk通常有三个副本。
7 k- t2 z6 z* {$ OGFS Master的修改操作总是先记录操作日志,然后修改内存。当Master发生故障
7 t/ X2 G2 f; G( Y" N7 _* P' }9 I4 v重启时,可以通过磁盘中的操作日志恢复内存数据结构。另外,为了减少Master宕机, \1 Z0 l7 N v' {
恢复时间,Master会定期将内存中的数据以checkpoint文件的形式转储到磁盘中,从5 v9 o; g! u7 [5 \
而减少回放的日志量。为了进一步提高Master的可靠性和可用性,GFS中还会执行实
, T1 c0 r: N0 j6 y) z3 j. u. u5 m时热备,所有的元数据修改操作都必须保证发送到实时热备才算成功。远程的实时9 q# {* @+ H6 q7 K# \& @1 s4 V3 V
热备将实时接收Master发送的操作日志并在内存中回放这些元数据操作。如果Master
3 a- S$ X/ X/ A3 i! c, s+ n宕机,还可以秒级切换到实时备机继续提供服务。为了保证同一时刻只有一台
) V; Y# Q7 y( [/ x# zMaster,GFS依赖Google内部的Chubby服务进行选主操作。
# M" V4 C) E& i# YMaster需要持久化前两种元数据,即命名空间及文件到chunk之间的映射;对于5 \: X; p& M- ]: n7 _% L( L& B% ]
第三种元数据,即chunk副本的位置信息,Master可以选择不进行持久化,这是因为
( B( c2 h- w# `) e6 s5 kChunkServer维护了这些信息,即使Master发生故障,也可以在重启时通过
* y+ _9 y3 a* S# Q" y5 c2 q0 ZChunkServer汇报来获取。+ l2 H' z) L- j9 T" g8 c
(2)ChunkServer容错
3 s& Y+ b3 K: ^GFS采用复制多个副本的方式实现ChunkServer的容错,每个chunk有多个存储副
* _0 o$ T7 p7 m7 s+ A本,分别存储在不同的ChunkServer上。对于每个chunk,必须将所有的副本全部写入2 E" x$ f% y1 G1 }+ p" D
成功,才视为成功写入。如果相关的副本出现丢失或不可恢复的情况,Master自动将; j% Q$ y/ k1 h- @/ d+ M
副本复制到其他ChunkServer,从而确保副本保持一定的个数。
# {% u; v- J# n' S) W# d% C5 R9 d另外,ChunkServer会对存储的数据维持校验和。GFS以64MB为chunk大小来划分
: T. Q4 r# \; T7 d: U' r0 z. [文件,每个chunk又以Block为单位进行划分,Block大小为64KB,每个Block对应一个
* D* n( ]3 H) J% I/ l32位的校验和。当读取一个chunk副本时,ChunkServer会将读取的数据和校验和进行% P3 V7 O9 S8 o. J% r. Z) ~- o
比较,如果不匹配,就会返回错误,客户端将选择其他ChunkServer上的副本。( b, e$ x$ x5 e% }! C! U
4.1.3 Master设计
: l Q7 V! }" u+ Z, @, h2 ^/ M7 J1.Master内存占用: K9 v. W" m& q2 S6 ^" V
Master维护了系统中的元数据,包括文件及chunk命名空间、文件到chunk之间的
6 H0 I8 P, W# x, ~, f映射、chunk副本的位置信息。其中前两种元数据需要持久化到磁盘,chunk副本的位
' _" G3 r0 @& r1 N0 `置信息不需要持久化,可以通过ChunkServer汇报获取。# Y) Z# H G) R/ O- G
内存是Master的稀有资源,接下来介绍如何估算Master的内存使用量。chunk的元 h k$ @5 u, w; H F1 |) m6 D
信息包括全局唯一的ID、版本号、每个副本所在的ChunkServer编号、引用计数等。' G9 [3 `4 B9 R
GFS系统中每个chunk大小为64MB,默认存储3份,每个chunk的元数据小于64字节。
- h) j, ~( J# R% G那么1PB数据的chunk元信息大小不超过1PB×3/64MB×64=3GB。另外,Master对命名
. N k1 r9 U- Q4 \& l空间进行了压缩存储,例如有两个文件foo1和foo2都存放在目$ o* _: r8 S8 H1 E
录/home/very_long_directory_name/中,那么目录名在内存中只需要存放一次。压缩存
. J3 y) ?) n/ l+ d% u储后,每个文件在文件命名空间的元数据也不超过64字节,由于GFS中的文件一般都
( W/ E" m) S, q+ U是大文件,因此,文件命名空间占用内存不多。这也就说明了Master内存容量不会成2 f7 k! G- j3 y# [$ _' I3 C+ f
为GFS的系统瓶颈。" c8 S) j% [6 |+ p: j8 m( G f
2.负载均衡
9 @9 d- t1 O# @ k) dGFS中副本的分布策略需要考虑多种因素,如网络拓扑、机架分布、磁盘利用率' I9 D1 M. X( y+ w h& H+ [
等。为了提高系统的可用性,GFS会避免将同一个chunk的所有副本都存放在同一个3 O8 S/ r% j w- r$ u' y
机架的情况。' J* B0 P+ E1 ~+ `
系统中需要创建chunk副本的情况有三种:chunk创建、chunk复制(re-
- M9 S' t5 R5 R, Dreplication)以及负载均衡(rebalancing)。
3 y# P8 U0 q+ \ S; a# S- t当Master创建了一个chunk,它会根据如下因素来选择chunk副本的初始位置:1)7 u+ |0 w1 N' [4 M2 T
新副本所在的ChunkServer的磁盘利用率低于平均水平;2)限制每个Chunk-Server“最3 M2 s9 h) l- E
近”创建的数量;3)每个chunk的所有副本不能在同一个机架。第二点容易忽略但却
+ n. a* x9 g& q( D' Y0 E很重要,因为创建完chunk以后通常需要马上写入数据,如果不限制“最近”创建的数1 L! B U" {; P% {- x$ ]
量,当一台空的ChunkServer上线时,由于磁盘利用率低,可能导致大量的chunk瞬间
7 c9 c7 D- H$ ~0 u- q3 u迁移到这台机器从而将它压垮。5 \! _& W/ R% y
当chunk的副本数量小于一定的数量后,Master会尝试重新复制一个chunk副本。
( k, L6 M' R% w9 C. d) [可能的原因包括ChunkServer宕机或者ChunkServer报告自己的副本损坏,或者/ t+ j' V7 v5 {/ I1 z c4 `
ChunkServer的某个磁盘故障,或者用户动态增加了chunk的副本数,等等。每个chunk
" p. k1 H# p2 C' b5 }) C, A复制任务都有一个优先级,按照优先级从高到低在Master排队等待执行。例如,只有
) f" t. v+ P/ w% D! ~! {/ z L一个副本的chunk需要优先复制。另外,GFS会提高所有阻塞客户端操作的chunk复制; K/ P9 f* n# |1 r# R8 W
任务的优先级,例如客户端正在往一个只有一个副本的chunk追加数据,如果限制至 o9 S1 E! w; y
少需要追加成功两个副本,那么这个chunk复制任务会阻塞客户端写操作,需要提高
3 k+ C3 _4 G2 R. ~优先级。
" w3 _6 x$ W3 E" C/ S$ @最后,Master会定期扫描当前副本的分布情况,如果发现磁盘使用量或者机器负
: g( g+ g" L0 T/ a1 Q% L% ~# l载不均衡,将执行重新负载均衡操作。
" `6 f. c' ~$ t. C z无论是chunk创建,chunk重新复制,还是重新负载均衡,这些操作选择chunk副本9 i2 |8 x ] {, K" h% c* e9 o. S
位置的策略都是相同的,并且需要限制重新复制和重新负载均衡任务的拷贝速度,9 V, ?; P/ I) G) O
否则可能影响系统正常的读写服务。- e7 S& B% g, J" a9 N* g- ?2 c
3.垃圾回收
9 F5 b# }- M6 d" Y* b; c1 p/ A) DGFS采用延迟删除的机制,也就是说,当删除文件后,GFS并不要求立即归还可5 u- W5 |. N8 w# c
用的物理存储,而是在元数据中将文件改名为一个隐藏的名字,并且包含一个删除
% @0 t& Z3 L' @0 Y6 G y# r时间戳。Master定时检查,如果发现文件删除超过一段时间(默认为3天,可配
9 p8 U! W: }! b; g& ]1 G置),那么它会把文件从内存元数据中删除,以后ChunkServer和Master的心跳消息% Z5 d0 f5 Z; ]
中,每一个ChunkServer都将报告自己的chunk集合,Master会回复在Master元数据中+ T3 Y9 V2 W9 H9 x& Q2 k7 U
已经不存在的chunk信息,这时,ChunkServer会释放这些chunk副本。为了减轻系统的" D0 W- Z2 q' Y9 b: Q3 ~
负载,垃圾回收一般在服务低峰期执行,比如每天晚上凌晨1:00开始。
% o7 u1 J' L, J# B另外,chunk副本可能会因为ChunkServer失效期间丢失了对chunk的修改操作而导7 r7 M/ p5 z: X0 S8 b9 h& k4 {9 F
致过期。系统对每个chunk都维护了版本号,过期的chunk可以通过版本号检测出来。% u% ?$ c1 s0 c6 e" X4 C
Master仍然通过正常的垃圾回收机制来删除过期的副本。9 t, | F; J4 ^$ r: Q6 _
4.快照
5 o: f, e$ M7 `快照(Snapshot)操作是对源文件/目录进行一个“快照”操作,生成该时刻源文/ x9 _1 u$ L' V7 Q& P( i; T: Y
件/目录的一个瞬间状态存放于目标文件/目录中。GFS中使用标准的写时复制机制生3 Z8 s3 R0 R7 c) u @7 t
成快照,也就是说,“快照”只是增加GFS中chunk的引用计数,表示这个chunk被快照
8 j0 P2 c: \4 b. O6 e6 f- g# e文件引用了,等到客户端修改这个chunk时,才需要在ChunkServer中拷贝chunk的数据" Q d/ P0 T- R
生成新的chunk,后续的修改操作落到新生成的chunk上。
/ q$ U+ l; U. H为了对某个文件做快照,首先需要停止这个文件的写服务,接着增加这个文件
" J, j+ W3 {9 M的所有chunk的引用计数,以后修改这些chunk时会拷贝生成新的chunk。对某个文件执
6 ]6 k5 p; L, L+ @, g( \7 F9 |4 [行快照的大致步骤如下:5 n" q( y3 f# ], D: u
1)通过租约机制收回对文件的每个chunk写权限,停止对文件的写服务;/ a, @% W1 n6 `
2)Master拷贝文件名等元数据生成一个新的快照文件;' B/ V, E/ |8 z! U( A4 p4 _; L3 G
3)对执行快照的文件的所有chunk增加引用计数。
0 [- G: G! G4 t* T" a i例如,对文件foo执行快照操作生成foo_backup,foo在GFS中有三个chunk:C1、C2
" \4 v$ u7 T: c. v% G5 j" ?和C3(简单起见,假设每个chunk只有一个副本)。Master首先需要收回C1、C2和C3
* l7 V3 d8 b% V: f! M2 M的写租约,从而保证文件foo处于一致的状态,接着Master复制foo文件的元数据用于" X1 P& v0 s6 U0 }3 `
生成foo_backup,foo_backup同样指向C1、C2和C3。快照前,C1、C2和C3只被一个文1 T0 V r+ d9 |8 P0 t: V7 {- Q
件foo引用,因此引用计数为1;执行快照操作后,这些chunk的引用计数增加为2。以6 E7 U, G. o( w$ u/ s( i8 ]. Q
后客户端再次向C3追加数据时,Master发现C3的引用计数大于1,通知C3所在的7 V( _: }5 `+ Q2 r1 U6 h! U& c
ChunkServer本次拷贝C3生成C3',客户端的追加操作也相应地转向C3'。
) Z; N( }2 B, G7 L9 V4.1.4 ChunkServer设计
7 ]5 {/ Z; ?, g/ K+ f4 a( Q# m; ]: oChunkServer管理大小约为64MB的chunk,存储的时候需要保证chunk尽可能均匀( n4 A( r% l8 k
地分布在不同的磁盘之中,需要考虑的可能因素包括磁盘空间、最近新建chunk数
1 g1 s) [* X) `/ S' b2 q: {" Q等。另外,Linux文件系统删除64MB大文件消耗的时间太长且没有必要,因此,删除
8 x3 {) ?& c8 E- ~% D Echunk时可以只将对应的chunk文件移动到每个磁盘的回收站,以后新建chunk的时候可
( c5 F6 D/ M1 @, k4 H* z以重用。" W" Y, @0 X: v% K0 S
ChunkServer是一个磁盘和网络IO密集型应用,为了最大限度地发挥机器性能,
. S7 `/ v2 w* i/ r9 [" {需要能够做到将磁盘和网络操作异步化,但这会增加代码实现的难度。
% h D7 T* Y% U" F& s: t4.1.5 讨论
/ O: q; @4 `* f c" j3 A) G从GFS的架构设计可以看出,GFS是一个具有良好可扩展性并能够在软件层面自
a }" x5 A( h动处理各种异常情况的系统。Google是一家很重视自动化的公司,从早期的GFS,再& c+ T' A' ], ^8 P3 Z
到Bigtable、Megastore,以及最近的Spanner,Google的分布式存储系统在这一点上一脉+ @0 o5 k) `2 X3 w# S$ f& T/ | `
相承。由于Google的系统一开始能很好地解决可扩展性问题,所以后续的系统能够, }& C4 S/ |1 @% E, r; E
构建在前一个系统之上并且一步一步引入新的功能,如Bigtable在GFS之上将海量数
% f( C$ u/ Q8 }9 [6 B* @据组织成表格形式,Megastore、Spanner又进一步在Bigtable之上融合一些关系型数据: d# L% v1 H2 g, z9 v' C# G
库的功能,整个解决方案完美华丽。
2 Z- X4 X$ T" B4 ~1 D/ g \自动化对系统的容错能力提出了很高的要求,在设计GFS时认为节点失效是常
* P r4 U0 M1 @1 p! c态,通过在软件层面进行故障检测,并且通过chunk复制操作将原有故障节点的服务( m$ W( i5 z O: n5 ]- L$ U6 z$ ^- N
迁移到新的节点。系统还会根据一定的策略,如磁盘使用情况、机器负载等执行负
3 L! Q ^" o& O, J$ O! W载均衡操作。Google在软件层面的努力获得了巨大的回报,由于软件层面能够做到
' D7 B/ q1 ]) @. B% e+ Q2 B自动化容错,底层的硬件可以采用廉价的错误率较高的硬件,比如廉价的SATA盘,3 u8 X0 c( ~: r. T9 T
这大大降低了云服务的成本,在和其他厂商的竞争中表现出价格优势。比较典型的$ K6 j& m9 h# z' B6 {
例子就是Google的邮箱服务,由于基础设施成本低,Gmail服务能够免费给用户提供. ^ o) L7 M* m. Z" O/ R7 X% k9 }
更大的容量,令其他厂商望尘莫及。9 C! @, w5 e2 @/ a/ e4 b
Google的成功经验也表明了一点:单Master的设计是可行的。单Master的设计不
) q; {/ d. ]3 l% z% G仅简化了系统,而且能够较好地实现一致性,后面我们将要看到的绝大多数分布式7 m" ~1 v$ t7 X' r7 \, M g
存储系统都和GFS一样依赖单总控节点。然而,单Master的设计并不意味着实现GFS* M; _. ~1 q( T6 @
只是一些比较简单琐碎的工作。基于性能考虑,GFS提出了“记录至少原子性追加一$ s4 z' A/ d' S
次”的一致性模型,通过租约的方式将每个chunk的修改授权下放到ChunkServer从而减
$ ], D& G: s0 ~7 x# _5 x4 b少Master的负载,通过流水线的方式复制多个副本以减少延时,追加流程复杂繁琐。
. @; v, k9 c' r3 i. i; U另外,Master维护的元数据有很多,需要设计高效的数据结构,占用内存小,并且能2 a j& A& l' y
够支持快照操作。支持写时复制的B树能够满足Master的元数据管理需求,然而,它# f) A7 E# j1 Z" f5 R
的实现是相当复杂的。2 {6 h. U& ^! m1 {
$ i2 m. | s! g1 {% n |
|