|
分布式文件系统的主要功能有两个:一个是存储文档、图像、视频之类的Blob$ O9 c9 F2 W) F& `
类型数据;另外一个是作为分布式表格系统的持久化层。
8 {, u; o: N$ b$ I分布式文件系统中最为著名的莫过于Google File System(GFS),它构建在廉价
6 I: z5 N5 R# l& u# Z B的普通PC服务器之上,支持自动容错。GFS内部将大文件划分为大小约为64MB的数- ^0 U. x0 M" _" S3 w
据块(chunk),并通过主控服务器(Master)实现元数据管理、副本管理、自动负
5 `( b8 Q& M' n7 i) Z+ M3 z载均衡等操作。其他文件系统,例如Taobao File System(TFS)、Facebook Haystack8 g1 G" }7 M6 G% U; f. W; ~
或多或少借鉴了GFS的思路,架构上都比较相近。
$ q) d$ l. K" }! Q本章首先重点介绍GFS的内部实现机制,接着介绍TFS和Face book Haystack的内, L+ p9 G3 Z0 _+ v+ }0 ?
部实现。最后,本章还会简单介绍内容分发网络(Content Delivery Network,CDN)技8 J# F& s* M! O# `8 H" i
术,这种技术能够将图像、视频之类的数据缓存在离用户“最近”的网络节点上,从0 I, R7 O3 o; |" x _- C! e7 i
而降低访问延时,节省带宽。
! L) {3 [* r0 f- f% j" x; G, ^4.1 Google文件系统
. p% ~! G) t1 yGoogle文件系统(GFS)是构建在廉价服务器之上的大型分布式系统。它将服务
4 B3 [, k$ c1 o器故障视为正常现象,通过软件的方式自动容错,在保证系统可靠性和可用性的同- J" o/ W f c! C% y9 B; Y) e5 s
时,大大降低系统的成本。+ @" Q4 J8 j6 K$ P6 T2 U
GFS是Google分布式存储的基石,其他存储系统,如Google Bigtable、Google- s+ Q7 P$ T7 y1 ]0 N+ c2 ?
Megastore、Google Percolator均直接或者间接地构建在GFS之上。另外,Google大规
; a) z8 `7 V7 m模批处理系统MapReduce也需要利用GFS作为海量数据的输入输出。2 c1 V9 A: t9 j6 b3 D& i! u/ W
4.1.1 系统架构! b0 c5 w8 ]- f$ F) J
如图4-1所示,GFS系统的节点可分为三种角色:GFS Master(主控服务器)、9 Q' F& k% Y/ K9 J
GFS ChunkServer(CS,数据块服务器)以及GFS客户端。3 u6 k3 ]% ~6 s( C# c
图 4-1 GFS整体架构$ q5 I$ D4 N8 v- `
GFS文件被划分为固定大小的数据块(chunk),由主服务器在创建时分配一个
3 T5 ]2 Q0 o# w' ^64位全局唯一的chunk句柄。CS以普通的Linux文件的形式将chunk存储在磁盘中。为
/ q; h; X: t5 w了保证可靠性,chunk在不同的机器中复制多份,默认为三份。
! {& `9 g. ?9 A2 ?4 h主控服务器中维护了系统的元数据,包括文件及chunk命名空间、文件到chunk之
8 Q2 l7 x* z$ c3 Q间的映射、chunk位置信息。它也负责整个系统的全局控制,如chunk租约管理、垃圾1 s% y% _( X0 C3 g7 k
回收无用chunk、chunk复制等。主控服务器会定期与CS通过心跳的方式交换信息。
4 }+ L( E9 M) Y+ s$ f0 U客户端是GFS提供给应用程序的访问接口,它是一组专用接口,不遵循POSIX规- h/ z; A4 E1 e: F9 w- _
范,以库文件的形式提供。客户端访问GFS时,首先访问主控服务器节点,获取与之: E8 S8 G, J D- a: p0 h5 L
进行交互的CS信息,然后直接访问这些CS,完成数据存取工作。
: o9 \9 ^( s+ k0 P1 e! ?需要注意的是,GFS中的客户端不缓存文件数据,只缓存主控服务器中获取的元
; g% r5 Y2 {& h& S; J数据,这是由GFS的应用特点决定的。GFS最主要的应用有两个:MapReduce与
1 ]9 j+ z- l. g- e, |% s0 K. zBigtable。对于MapReduce,GFS客户端使用方式为顺序读写,没有缓存文件数据的必
8 L$ h+ ^6 P+ V2 `* }要;而Bigtable作为分布式表格系统,内部实现了一套缓存机制。另外,如何维护客& o* Q r; ]% i: _% n2 r
户端缓存与实际数据之间的一致性是一个极其复杂的问题。
5 b7 a: d H. K; T% [4.1.2 关键问题$ {: {& \) z- j: T
1.租约机制
f1 d2 j1 M, nGFS数据追加以记录为单位,每个记录的大小为几十KB到几MB不等,如果每次
3 a+ M$ B4 C6 ?. D7 }& u记录追加都需要请求Master,那么Master显然会成为系统的性能瓶颈,因此,GFS系
6 P9 |. L, S* |6 g2 k8 O统中通过租约(lease)机制将chunk写操作授权给ChunkServer。拥有租约授权的
k* [" t% q- p+ ]- FChunkServe称为主ChunkServer,其他副本所在的ChunkServer称为备ChunkServer。租
* k+ x. q& t+ ~$ e1 s约授权针对单个chunk,在租约有效期内,对该chunk的写操作都由主ChunkServer负
9 R. d' |/ f0 H8 Q5 M( z责,从而减轻Master的负载。一般来说,租约的有效期比较长,比如60秒,只要没有
* a6 g( G' B" Q4 u4 u% E: D- y/ W出现异常,主ChunkServer可以不断向Master请求延长租约的有效期直到整个chunk写: G$ j% i& c: T1 d1 I% c" k2 A
满。5 H' z+ u0 L! \! Q, c! u
假设chunk A在GFS中保存了三个副本A1、A2、A3,其中,A1是主副本。如果副0 q0 E& B" L( U
本A2所在ChunkServer下线后又重新上线,并且在A2下线的过程中,副本A1和A3有$ n+ G k9 V7 t+ X: i3 x
更新,那么A2需要被Master当成垃圾回收掉。GFS通过对每个chunk维护一个版本号
3 z5 n5 x! R) ~" s4 W7 Z+ _来解决,每次给chunk进行租约授权或者主ChunkServer重新延长租约有效期时,
+ j5 F( A! [5 _+ x8 \Master会将chunk的版本号加1。A2下线的过程中,副本A1和A3有更新,说明主
- f+ V9 N+ p5 G6 ~$ G& O& S, B+ I/ nChunkServer向Master重新申请租约并增加了A1和A3的版本号,等到A2重新上线后,$ X6 O6 ?" z! E3 r! [# s
Master能够发现A2的版本号太低,从而将A2标记为可删除的chunk,Master的垃圾回收! U: O9 D# V5 V% o
任务会定时检查,并通知ChunkServer将A2回收掉。
% l+ B6 S3 `: ~2 p! n& l2.一致性模型6 V* J' Y5 I7 k( D
GFS主要是为了追加(append)而不是改写(overwrite)而设计的。一方面是因! C, M8 ~+ x2 n6 s) E6 ]" Z7 }
为改写的需求比较少,或者可以通过追加来实现,比如可以只使用GFS的追加功能构* H# b5 g4 ^: c5 A* ]
建分布式表格系统Bigtable;另一方面是因为追加的一致性模型相比改写要更加简单
" h |. o+ R+ O L: w有效。考虑chunk A的三个副本A1、A2、A3,有一个改写操作修改了A1、A2但没有
/ D. `- u- W! d- e) Q& K3 Y" w4 Q修改A3,这样,落到副本A3的读操作可能读到不正确的数据;相应地,如果有一个
0 S2 Y4 `0 S8 T0 O x! J5 l7 x2 C) p追加操作往A1、A2上追加了一个记录,但是追加A3失败,那么即使读操作落到副本
4 _0 y' H8 {3 R, rA3也只是读到过期而不是错误的数据。4 D1 v1 p6 Y m$ I4 L x
我们只讨论追加的一致性。如果不发生异常,追加成功的记录在GFS的各个副本
( D8 Z$ q2 y" J* b% c# c中是确定并且严格一致的;但是如果出现了异常,可能出现某些副本追加成功而某2 y' p* C0 e- r7 P) u4 N3 o
些副本没有成功的情况,失败的副本可能会出现一些可识别的填充(padding)记; R8 q. t6 F9 A) z$ v6 o- b
录。GFS客户端追加失败将重试,只要返回用户追加成功,说明在所有副本中都至少
I. K# T: b# D6 A2 @追加成功了一次。当然,可能出现记录在某些副本中被追加了多次,即重复记录;' x9 F" b/ M9 X
也可能出现一些可识别的填充记录,应用层需要能够处理这些问题。
& ^* B; {5 n7 M( w* \! `1 z另外,由于GFS支持多个客户端并发追加,多个客户端之间的顺序是无法保证
1 u+ f" ~" X9 W6 c" T1 C的,同一个客户端连续追加成功的多个记录也可能被打断,比如客户端先后追加成
: c2 l- y$ w* L$ \功记录R1和R2,由于追加R1和R2这两条记录的过程不是原子的,中途可能被其他客/ p, p7 ^( o1 J# M+ W
户端打断,那么GFS的chunk中记录的R1和R2可能不连续,中间夹杂着其他客户端追/ ~- {' l& _& c: w; x
加的数据。
0 w; K/ G- H# T1 i* DGFS的这种一致性模型是追求性能导致的,这增加了应用程序开发的难度。对于( ]: c- V6 a4 Z
MapReduce应用,由于其批处理特性,可以先将数据追加到一个临时文件,在临时文
1 ~1 l4 e2 P$ U: l件中维护索引记录每个追加成功的记录的偏移,等到文件关闭时一次性将临时文件
! I a& d6 q/ F* K. i0 r& @* P改名为最终文件。对于上层的Bigtable,有两种处理方式,后面将会介绍。
. m$ [9 V) J I+ N3.追加流程* F; A, N U6 L. Q* \$ \) O; X
追加流程是GFS系统中最为复杂的地方,而且,高效支持记录追加对基于GFS实8 p* v' p1 Q, P6 a6 j
现的分布式表格系统Bigtable是至关重要的。如图4-2所示,追加流程大致如下:% p7 f5 ?7 V6 G& l) \8 v2 J
图 4-2 GFS追加流程
: {: `- q" S% Y' G- i" ^1)客户端向Master请求chunk每个副本所在的ChunkServer,其中主ChunkServer持
' Z4 O8 n9 h+ k; a, `2 N有修改租约。如果没有ChunkServer持有租约,说明该chunk最近没有写操作,Master
6 h6 b7 T V# M) m: [5 [. [+ A会发起一个任务,按照一定的策略将chunk的租约授权给其中一台ChunkServer。
8 H( `# U# ~: [; b X& c: Y2)Master返回客户端主副本和备副本所在的ChunkServer的位置信息,客户端将" X) g8 J: t( N) ^* Z0 d5 f
缓存这些信息供以后使用。如果不出现故障,客户端以后读写该chunk都不需要再次
- ]0 [ D H# s请求Master。
0 Y$ W4 \- M0 l9 ~$ q' C3)客户端将要追加的记录发送到每一个副本,每一个ChunkServer会在内部的
. Z4 L, v6 N" g5 VLRU结构中缓存这些数据。GFS中采用数据流和控制流分离的方法,从而能够基于网
1 ?; R2 G* n* R% _' p4 h2 |! Q+ m络拓扑结构更好地调度数据流的传输。
. l% e4 P- k, \* ]9 O* t, M% b4)当所有副本都确认收到了数据,客户端发起一个写请求控制命令给主副本。6 L+ T L! |9 x; T% ?3 O0 q: `
由于主副本可能收到多个客户端对同一个chunk的并发追加操作,主副本将确定这些
" g, a. @ }( s操作的顺序并写入本地。* ]" }# F9 v* u$ m: S
5)主副本把写请求提交给所有的备副本。每一个备副本会根据主副本确定的顺- f" z/ K# l8 }
序执行写操作。/ o" K+ U% ?& Z9 B5 F
6)备副本成功完成后应答主副本。
' ?0 o; r) R o4 I' a2 A7)主副本应答客户端,如果有副本发生错误,将出现主副本写成功但是某些备' b& Y E9 j; |; }" P0 ]4 {7 L H
副本不成功的情况,客户端将重试。
7 I" M. {0 N' h" \GFS追加流程有两个特色:流水线及分离数据流与控制流。流水线操作用来减少
; x- }8 z# E- @8 K; K( x6 Z延时。当一个ChunkServer接收到一些数据,它就立即开始转发。由于采用全双工网
, M9 o9 t5 s7 d% R络,立即发送数据并不会降低接收数据的速率。抛开网络阻塞,传输B个字节到R个' D8 P# c" K' ^4 c/ _8 T
副本的理想时间是B/T+RL,其中T是网络吞吐量,L是节点之间的延时。假设采用千% ?* I- a1 U4 C% s- u) H: G, X
兆网络,L通常小于1ms,传输1MB数据到多个副本的时间小于80ms。分离数据流与; d. |; v! A' ^" b' h5 `! z* N
控制流主要是为了优化数据传输,每一台机器都是把数据发送给网络拓扑图上“最+ k2 H: c- D4 g
近”的尚未收到数据的数据。举个例子,假设有三台ChunkServer:S1、S2和S3,S1与( X$ `% ~. v/ `7 o. G) U
S3在同一个机架上,S2在另外一个机架上,客户端部署在机器S1上。如果数据先从) f1 h/ R9 h& W: h" ^
S1转发到S2,再从S2转发到S3,需要经历两次跨机架数据传输;相对地,按照GFS' A6 g/ D% @ x8 n7 h
中的策略,数据先发送到S1,接着从S1转发到S3,最后转发到S2,只需要一次跨机
% x* v0 A! j( H# d5 \/ s% H架数据传输。$ J: l3 q3 o3 r
分离数据流与控制流的前提是每次追加的数据都比较大,比如MapReduce批处理
3 }1 U8 x8 g8 c9 I) q: ^) {系统,而且这种分离增加了追加流程的复杂度。如果采用传统的主备复制方法,追
& s6 d; d1 M- f# g, S加流程会在一定程度上得到简化,如图4-3所示:: z4 x' L% N1 t; y) i' X) v
图 4-3 GFS追加流程(数据流与控制流合并); O* h$ Y5 I$ }+ c/ k2 O6 l
1)同图4-2 GFS追加流程:客户端向Master请求chunk每个副本所在的
/ l8 v/ W, B) }$ E6 oChunkServer。
3 k/ O k& o' V2 A2)同图4-2 GFS追加流程:Master返回客户端主副本和备副本所在ChunkServer的6 `, l3 I) e$ w0 w2 H* q O
位置信息。
4 m3 e' `* p# ^. ]+ T% g3)Client将待追加数据发送到主副本,主副本可能收到多个客户端的并发追加
7 ^( B) B! q; h! h, z |请求,需要确定操作顺序,并写入本地。% g+ i5 I' u+ d% x. s! b# A$ t( _
4)主副本将数据通过流水线的方式转发给所有的备副本。/ q+ C& Z* P+ ~, y
5)每个备副本收到待追加的记录数据后写入本地,所有副本都在本地写成功并
2 h0 ~7 y- ^5 E1 ~5 r/ d& k$ ?且收到后一个副本的应答消息时向前一个副本回应,比如图4-3中备副本A需要等待" ]4 ]+ \- {. [4 L0 S
备副本B应答成功且本地写成功后才可以应答主副本。$ |: m1 K/ T# t; q
6)主副本应答客户端。如果客户端在超时时间之内没有收到主副本的应答,说
/ k0 `' p, h3 I8 [6 H: u明发生了错误,需要重试。0 f h/ s, e, ]. o+ d
当然,实际的追加流程远远没有这么简单。追加的过程中可能出现主副本租约
* |6 M' Z) p9 g6 r过期而失去chunk修改操作的授权,以及主副本或者备副本所在的ChunkServer出现故) D0 M1 O/ S! Q; a0 X9 r7 y
障,等等。由于篇幅有限,追加流程的异常处理留作读者思考。# p7 {4 R: C; u8 ^; K$ T
4.容错机制
: M' K& {! J, t6 A2 }7 |& t8 O$ [(1)Master容错
. O5 ?1 f; v# nMaster容错与传统方法类似,通过操作日志加checkpoint的方式进行,并且有一( p9 V0 W O, y9 n& h1 X3 Z
台称为"Shadow Master"的实时热备。% ~: C9 `% ^3 N/ X. P
Master上保存了三种元数据信息:6 W( n8 Y6 ?1 A, Q5 S% n) @; J8 p
●命名空间(Name Space),也就是整个文件系统的目录结构以及chunk基本信
3 h( v/ u9 i5 ]2 L/ Q1 f% i息;
1 [: N" `8 D8 ?" |; q7 H●文件到chunk之间的映射;
6 |( Q" D5 z4 U* i" z9 D( @●chunk副本的位置信息,每个chunk通常有三个副本。# H. @. ^. h6 {6 g" r( @9 A
GFS Master的修改操作总是先记录操作日志,然后修改内存。当Master发生故障
2 n- `# N/ {: \1 p重启时,可以通过磁盘中的操作日志恢复内存数据结构。另外,为了减少Master宕机
# [' V4 O5 n' [. E# I( `, W恢复时间,Master会定期将内存中的数据以checkpoint文件的形式转储到磁盘中,从+ a; g0 U+ }. x# j- p( b9 {
而减少回放的日志量。为了进一步提高Master的可靠性和可用性,GFS中还会执行实( T7 s! c! Y6 ^% h( t
时热备,所有的元数据修改操作都必须保证发送到实时热备才算成功。远程的实时
8 ~2 ~/ S) M; A& o7 Y- h热备将实时接收Master发送的操作日志并在内存中回放这些元数据操作。如果Master
/ p+ ?/ m# x6 u0 M4 G( C宕机,还可以秒级切换到实时备机继续提供服务。为了保证同一时刻只有一台& n3 D; C5 x7 [; l
Master,GFS依赖Google内部的Chubby服务进行选主操作。7 H2 }( R& d4 ?$ t0 m6 J
Master需要持久化前两种元数据,即命名空间及文件到chunk之间的映射;对于2 H4 v: B5 F9 H3 n) y( O3 ?9 _
第三种元数据,即chunk副本的位置信息,Master可以选择不进行持久化,这是因为
0 X2 o+ y$ ^% u& Q+ `' H; H1 Z% WChunkServer维护了这些信息,即使Master发生故障,也可以在重启时通过
4 k$ ]0 @ Z' k5 a2 FChunkServer汇报来获取。
* N9 F5 q3 l' D/ P. Y2 H(2)ChunkServer容错
$ o- U: k6 L7 O7 H/ I4 N7 BGFS采用复制多个副本的方式实现ChunkServer的容错,每个chunk有多个存储副 v6 Y {3 e( S ~ r1 f3 T
本,分别存储在不同的ChunkServer上。对于每个chunk,必须将所有的副本全部写入# D) @( R( @4 U- J ]
成功,才视为成功写入。如果相关的副本出现丢失或不可恢复的情况,Master自动将
+ c4 V& N% j: n" I" U- F$ u0 B9 L# e$ _副本复制到其他ChunkServer,从而确保副本保持一定的个数。4 T& Y8 z& q7 E2 b6 P% ?
另外,ChunkServer会对存储的数据维持校验和。GFS以64MB为chunk大小来划分
# j+ h0 j" B9 B2 ]文件,每个chunk又以Block为单位进行划分,Block大小为64KB,每个Block对应一个
" x$ ?; K6 b0 j32位的校验和。当读取一个chunk副本时,ChunkServer会将读取的数据和校验和进行% _+ A3 t4 r8 T; ^$ u9 ?% l
比较,如果不匹配,就会返回错误,客户端将选择其他ChunkServer上的副本。! |% k& I- e: Y
4.1.3 Master设计
2 R- S, s8 Z5 l7 W1.Master内存占用
, z. ?- W7 D6 l% GMaster维护了系统中的元数据,包括文件及chunk命名空间、文件到chunk之间的$ N' b3 o3 n+ e
映射、chunk副本的位置信息。其中前两种元数据需要持久化到磁盘,chunk副本的位* r8 c; O% b3 |; P2 @
置信息不需要持久化,可以通过ChunkServer汇报获取。
# }: p O# n/ W8 j4 S内存是Master的稀有资源,接下来介绍如何估算Master的内存使用量。chunk的元
- v$ s3 Q9 c1 ^% }3 t信息包括全局唯一的ID、版本号、每个副本所在的ChunkServer编号、引用计数等。2 `0 D" L; J. ^' _6 A L8 F1 u7 x
GFS系统中每个chunk大小为64MB,默认存储3份,每个chunk的元数据小于64字节。
8 G F: W: {7 \) |. G那么1PB数据的chunk元信息大小不超过1PB×3/64MB×64=3GB。另外,Master对命名( b3 p* {7 C* T, @
空间进行了压缩存储,例如有两个文件foo1和foo2都存放在目& g2 @4 M. P7 e% ] t _
录/home/very_long_directory_name/中,那么目录名在内存中只需要存放一次。压缩存
5 F' v% D% M/ R V3 O6 x储后,每个文件在文件命名空间的元数据也不超过64字节,由于GFS中的文件一般都
5 o& Q8 g' ]. \* o, S6 a5 s是大文件,因此,文件命名空间占用内存不多。这也就说明了Master内存容量不会成
' T r6 a* e1 A* r9 T为GFS的系统瓶颈。
5 Y! _5 v: \5 s5 C+ ^& K- B! m* t2.负载均衡; d- c: z+ k% T2 h" Q
GFS中副本的分布策略需要考虑多种因素,如网络拓扑、机架分布、磁盘利用率4 k+ K7 ^9 i( E: Y4 n1 _
等。为了提高系统的可用性,GFS会避免将同一个chunk的所有副本都存放在同一个
5 d" }' g2 c) W f0 i1 y. i机架的情况。, X3 j; F$ {/ w
系统中需要创建chunk副本的情况有三种:chunk创建、chunk复制(re-
$ @3 }2 L; @( c+ V) Y+ ireplication)以及负载均衡(rebalancing)。
8 J& f/ _3 f* N+ e' t; C当Master创建了一个chunk,它会根据如下因素来选择chunk副本的初始位置:1), _. A+ C2 h; j( [& X; T; e$ P
新副本所在的ChunkServer的磁盘利用率低于平均水平;2)限制每个Chunk-Server“最
& G7 y4 F% v: @2 n- d3 i近”创建的数量;3)每个chunk的所有副本不能在同一个机架。第二点容易忽略但却
/ }! w8 R4 i5 W" s- d1 v3 N2 A很重要,因为创建完chunk以后通常需要马上写入数据,如果不限制“最近”创建的数
- t7 P4 y1 l4 P" {量,当一台空的ChunkServer上线时,由于磁盘利用率低,可能导致大量的chunk瞬间
8 ~+ X& G0 W1 Y! v" W迁移到这台机器从而将它压垮。
$ {8 k' W. \8 K1 u( E% M当chunk的副本数量小于一定的数量后,Master会尝试重新复制一个chunk副本。" z7 C8 C* ]& @5 B/ \' v
可能的原因包括ChunkServer宕机或者ChunkServer报告自己的副本损坏,或者' X7 E& x( y; d9 Z' d
ChunkServer的某个磁盘故障,或者用户动态增加了chunk的副本数,等等。每个chunk Q9 x+ V& Y. L1 Y
复制任务都有一个优先级,按照优先级从高到低在Master排队等待执行。例如,只有
9 R4 K2 W/ J+ }1 S( M一个副本的chunk需要优先复制。另外,GFS会提高所有阻塞客户端操作的chunk复制
* p v T) ?( t- T8 z# \任务的优先级,例如客户端正在往一个只有一个副本的chunk追加数据,如果限制至# }7 {0 q8 O- B# s9 M6 I0 j
少需要追加成功两个副本,那么这个chunk复制任务会阻塞客户端写操作,需要提高 P$ H% J8 J. a9 x/ Y- T( N
优先级。
9 K1 ^2 o2 Y0 z6 K) P* B8 X最后,Master会定期扫描当前副本的分布情况,如果发现磁盘使用量或者机器负- {0 c. _0 J& l( {
载不均衡,将执行重新负载均衡操作。& [( O; b) ?2 k. s" m. H5 w
无论是chunk创建,chunk重新复制,还是重新负载均衡,这些操作选择chunk副本
/ H8 x4 F, L+ ?0 N7 ^" F7 t位置的策略都是相同的,并且需要限制重新复制和重新负载均衡任务的拷贝速度,
; X$ W: E" b' L1 l a3 O5 x否则可能影响系统正常的读写服务。' I% K3 j" T, {9 Q
3.垃圾回收
) Y" [ Q0 X8 g% w- A7 pGFS采用延迟删除的机制,也就是说,当删除文件后,GFS并不要求立即归还可" j) R8 p( `/ s+ C# l
用的物理存储,而是在元数据中将文件改名为一个隐藏的名字,并且包含一个删除; z, W4 `4 A- k
时间戳。Master定时检查,如果发现文件删除超过一段时间(默认为3天,可配# p, T% m7 D1 `4 ?, a1 L$ u8 \
置),那么它会把文件从内存元数据中删除,以后ChunkServer和Master的心跳消息
+ R8 i- E' \. j& T, X7 _% }中,每一个ChunkServer都将报告自己的chunk集合,Master会回复在Master元数据中
) u9 |6 l _/ E9 z已经不存在的chunk信息,这时,ChunkServer会释放这些chunk副本。为了减轻系统的
2 i+ s! b u) X负载,垃圾回收一般在服务低峰期执行,比如每天晚上凌晨1:00开始。9 E9 N# [! ^; b i4 `% y
另外,chunk副本可能会因为ChunkServer失效期间丢失了对chunk的修改操作而导8 z8 z# Y3 g5 l2 T, M
致过期。系统对每个chunk都维护了版本号,过期的chunk可以通过版本号检测出来。
4 e6 S) G6 a8 D* L- R" v! ^& YMaster仍然通过正常的垃圾回收机制来删除过期的副本。* r9 B. o2 C% Z& _1 U& }% X
4.快照
! }. L8 I2 L3 P6 x0 x- |, C快照(Snapshot)操作是对源文件/目录进行一个“快照”操作,生成该时刻源文5 `1 |1 n, S; U
件/目录的一个瞬间状态存放于目标文件/目录中。GFS中使用标准的写时复制机制生
. u+ o: d! |; s. W8 c3 _+ L成快照,也就是说,“快照”只是增加GFS中chunk的引用计数,表示这个chunk被快照
* g9 y0 o. T3 s- H3 n文件引用了,等到客户端修改这个chunk时,才需要在ChunkServer中拷贝chunk的数据
- g( b/ }9 W2 Z生成新的chunk,后续的修改操作落到新生成的chunk上。: D# O3 V6 K! t! n) @/ S
为了对某个文件做快照,首先需要停止这个文件的写服务,接着增加这个文件) j& j* Y+ G" f v: V% K
的所有chunk的引用计数,以后修改这些chunk时会拷贝生成新的chunk。对某个文件执
0 C1 s, ~* j' p2 i4 r行快照的大致步骤如下:
! ~: z6 j; w7 q# c C3 t/ f" n1)通过租约机制收回对文件的每个chunk写权限,停止对文件的写服务;' F+ B' v4 t8 q5 [! ~/ i
2)Master拷贝文件名等元数据生成一个新的快照文件;1 o \4 E N$ r% b1 L* ~
3)对执行快照的文件的所有chunk增加引用计数。4 N1 h7 v) j1 I! V+ H/ H
例如,对文件foo执行快照操作生成foo_backup,foo在GFS中有三个chunk:C1、C2# b0 e- N. i8 [0 [1 D: ?8 a4 }1 p
和C3(简单起见,假设每个chunk只有一个副本)。Master首先需要收回C1、C2和C3. J7 _& S3 x. ]/ B
的写租约,从而保证文件foo处于一致的状态,接着Master复制foo文件的元数据用于
`0 d8 v( O; a9 K/ O. Q7 ~+ r6 [生成foo_backup,foo_backup同样指向C1、C2和C3。快照前,C1、C2和C3只被一个文% x$ y l. j' f& K" u, O
件foo引用,因此引用计数为1;执行快照操作后,这些chunk的引用计数增加为2。以4 F6 I, s& }& s" Z: X
后客户端再次向C3追加数据时,Master发现C3的引用计数大于1,通知C3所在的3 k& b* P2 n0 @3 @- e+ y6 k3 ^
ChunkServer本次拷贝C3生成C3',客户端的追加操作也相应地转向C3'。( @* r, h' F' R5 e7 W( e9 Q5 f* R
4.1.4 ChunkServer设计; T3 H P1 B5 a0 c4 U
ChunkServer管理大小约为64MB的chunk,存储的时候需要保证chunk尽可能均匀) T/ Y$ U. v. e- X; i# I0 r" o4 F
地分布在不同的磁盘之中,需要考虑的可能因素包括磁盘空间、最近新建chunk数8 d2 e1 Z" h% _, g
等。另外,Linux文件系统删除64MB大文件消耗的时间太长且没有必要,因此,删除
2 D; ~8 I# w8 E, W. ?1 k1 _0 cchunk时可以只将对应的chunk文件移动到每个磁盘的回收站,以后新建chunk的时候可
4 F, P3 @2 x+ i' D以重用。
; T- K$ @& s g5 [/ m3 kChunkServer是一个磁盘和网络IO密集型应用,为了最大限度地发挥机器性能,
) B' R( K" l }, z需要能够做到将磁盘和网络操作异步化,但这会增加代码实现的难度。$ Z: d0 Y6 _' W) ?4 J( ]
4.1.5 讨论& n6 a9 @' f5 [ P) K
从GFS的架构设计可以看出,GFS是一个具有良好可扩展性并能够在软件层面自
: v, \( F, q9 U+ R# _9 C3 S动处理各种异常情况的系统。Google是一家很重视自动化的公司,从早期的GFS,再
. l5 i+ W4 J1 t5 x! v到Bigtable、Megastore,以及最近的Spanner,Google的分布式存储系统在这一点上一脉
+ X5 i' V% V2 B9 A相承。由于Google的系统一开始能很好地解决可扩展性问题,所以后续的系统能够4 ?0 x. z% T. e5 _) g4 m3 g2 Y
构建在前一个系统之上并且一步一步引入新的功能,如Bigtable在GFS之上将海量数
L- {! ~ d: S据组织成表格形式,Megastore、Spanner又进一步在Bigtable之上融合一些关系型数据' k& _. _7 J5 [: M, j* \
库的功能,整个解决方案完美华丽。; l- x; E7 } S7 i6 G3 G- s
自动化对系统的容错能力提出了很高的要求,在设计GFS时认为节点失效是常
. N- v; \) G. ?/ d$ u5 t态,通过在软件层面进行故障检测,并且通过chunk复制操作将原有故障节点的服务
4 g# @7 R @9 B2 T- N5 j迁移到新的节点。系统还会根据一定的策略,如磁盘使用情况、机器负载等执行负
, ~1 Z! K* x5 f2 v; _" G载均衡操作。Google在软件层面的努力获得了巨大的回报,由于软件层面能够做到
$ \4 h( @7 T& l) Z. Y+ }自动化容错,底层的硬件可以采用廉价的错误率较高的硬件,比如廉价的SATA盘,- s/ o G- ~( E2 W% f
这大大降低了云服务的成本,在和其他厂商的竞争中表现出价格优势。比较典型的7 R, {! E, L4 W0 B5 U/ `- t9 h
例子就是Google的邮箱服务,由于基础设施成本低,Gmail服务能够免费给用户提供
' p( [" B( R& ^# m" p更大的容量,令其他厂商望尘莫及。
3 K! u) X/ W, W4 @4 T9 ]Google的成功经验也表明了一点:单Master的设计是可行的。单Master的设计不 C1 e9 z" m% U/ A
仅简化了系统,而且能够较好地实现一致性,后面我们将要看到的绝大多数分布式( w3 ~. R4 ]0 V( V: Y5 Z; D* @. a6 s, l& d
存储系统都和GFS一样依赖单总控节点。然而,单Master的设计并不意味着实现GFS
! X5 M+ c+ l* h; C" T只是一些比较简单琐碎的工作。基于性能考虑,GFS提出了“记录至少原子性追加一
3 B. T9 P& Z: R9 P/ K次”的一致性模型,通过租约的方式将每个chunk的修改授权下放到ChunkServer从而减, m" g1 t# h: j }
少Master的负载,通过流水线的方式复制多个副本以减少延时,追加流程复杂繁琐。( l0 G( w3 J6 s6 a! q5 l$ I2 ]! B
另外,Master维护的元数据有很多,需要设计高效的数据结构,占用内存小,并且能3 r% ?( T. Z' | k+ E! b' ^& W2 ^
够支持快照操作。支持写时复制的B树能够满足Master的元数据管理需求,然而,它- I, G. C' ?3 I5 Y' L0 L8 @) k$ x
的实现是相当复杂的。
& `5 K- H1 o( [& B6 X- Q" O) R+ G2 @% f
|
|