|
第6章 分布式表格系统
( P* e) b- q7 D" w* ~; A0 H; j0 D分布式表格系统对外提供表格模型,每个表格由很多行组成,通过主键唯一标1 V6 ^6 K! x. c: Z4 ]+ d$ M
识,每一行包含很多列。整个表格在系统中全局有序,适用3.3.2节中讲的顺序分
5 a. s% s7 N, [2 Z! D4 N布。. i5 m# f# P# `! u. ^: a
Google Bigtable是分布式表格系统的始祖,它采用双层结构,底层采用GFS作为
& J! t. s+ q; e2 u3 C持久化存储层。GFS+Bigtable双层架构是一种里程碑式的架构,其他系统,包括9 T, n$ O" ]5 F* _1 C, K( O
Microsoft分布式存储系统Windows Azure Storage以及开源的Hadoop系统,均为其模仿+ | Q& w0 a* U$ Q9 V7 _( g
者。Bigtable的问题在于对外接口不够丰富,因此,Google后续开发了两套系统,一" g' |8 a! I) L
套是Megastore,构建在Bigtable之上,提供更加丰富的使用接口;另外一套是
5 k7 H2 j2 E$ n" x4 \Spanner,支持跨多个数据中心的数据库事务,下一章会专门介绍。
% F1 ]. @4 @8 e- Y2 X/ {本章首先详细介绍Bigtable的架构及实现,接着分析Megastore的架构,最后介绍" a0 K6 n9 i- H% W' D K+ ^
Microsoft Azure Storage的架构。
& ] C& G* ?; u/ u0 {6.1 Google Bigtable- P, J) z0 S; t2 V# j* L
Bigtable是Google开发的基于GFS和Chubby的分布式表格系统。Google的很多数
* n* k2 \9 S8 k1 [$ N) i- O: g据,包括Web索引、卫星图像数据等在内的海量结构化和半结构化数据,都存储在
0 q- x c, q6 I4 o1 V" Z XBigtable中。与Google的其他系统一样,Bigtable的设计理念是构建在廉价的硬件之
0 ~7 |+ \2 J$ W$ E9 ~* \上,通过软件层面提供自动化容错和线性可扩展性能力。
+ c7 g/ d# A( B- x. K2 zBigtable系统由很多表格组成,每个表格包含很多行,每行通过一个主键(Row- K# C- b# V ]% f. U
Key)唯一标识,每行又包含很多列(Column)。某一行的某一列构成一个单元
- E, C. O% ?; O" ^(Cell),每个单元包含多个版本的数据。整体上看,Bigtable是一个分布式多维映: C$ T2 Q. u( d7 r" V
射表,如下所示:
' L1 H% d( R/ H5 K. W(row:string,column:string,timestamp:int64)->string
8 @! @- S/ D& J( m b另外,Bigtable将多个列组织成列族(column family),这样,列名由两个部分6 Q- q& Y+ [0 S; I' }" m
组成:(column family,qualifier)。列族是Bigtable中访问控制的基本单元,也就是
# U- |% B9 W; v$ b说,访问权限的设置是在列族这一级别上进行的。Bigtable中的列族在创建表格的时$ a* L) Q) L# D+ S
候需要预先定义好,个数也不允许过多;然而,每个列族包含哪些qualifier是不需要
* W; n2 n l% n2 o& L* X j预先定义的,qualifier可以任意多个,适合表示半结构化数据。# ]" N8 ?& G% G' o$ r
Bigtable数据的存储格式如图6-1所示。
( x8 f0 A- ^: k) e图 6-1 Bigtable数据存储格式2 i. b7 h; J$ f- J
Bigtable中的行主键可以是任意的字符串,最大不超过64KB。Bigtable表中的数
8 |% b# Q& K/ Y1 y9 K; ~7 }据按照行主键进行排序,排序使用的是字典序。图6-1中行主键com.cnn.www是域名
7 T2 m8 B c4 G6 i' Z: _% Jwww.cnn.com变换后的结果,这样做的好处是使得所有www.cnn.com下的子域名在系( B" ^5 U6 J0 y- s
统中连续存放。这一行数据包含两个列族:"contents"和"anchor"。其中,列( ?, \* h" t6 N; G2 d8 D1 x' q; l
族"anchor"又包含两个列,qualifier分别为"cnnsi.com"和"my:look.ca"。) E% a8 {/ \- [% }" _& z0 t% m
Google的很多服务,比如Web检索和用户的个性化设置,都需要保存不同时间的1 L, P3 N, H J- l' C
数据,这些不同的数据版本必须通过时间戳来区分。图6-1中的t 4 、t 5 和t 6 表示保存了
! M+ ~0 N/ K! E. l三个时间点获取的网页。为了简化不同版本的数据管理,Bigtable提供了两种设置:
- U) [2 I: B% \0 j4 ~: U" I一种是保留最近的N个不同版本,另一种是保留限定时间内的所有不同版本,比如可
1 W5 {$ V) ~, h, q8 E以保存最近10天的所有不同版本的数据。失效的版本将会由Bigtable的垃圾回收机制2 N& O/ B2 s" T7 Q! M& \
自动删除。8 F! L8 x o# l3 Q8 v. r
6.1.1 架构1 H* W" P; F6 x0 Q/ v) k1 q
Bigtable构建在GFS之上,为文件系统增加一层分布式索引层。另外,Bigtable依, q0 ]+ [# t1 ^. t" P
赖Google的Chubby(即分布式锁服务)进行服务器选举及全局信息维护。
; v9 s. ^) t' X5 I& O& H如图6-2所示,Bigtable将大表划分为大小在100~200MB的子表(tablet),每个$ `' [1 B) o+ M3 ~
子表对应一个连续的数据范围。Bigtable主要由三个部分组成:客户端程序库
5 p8 D+ X1 Y$ ? `(Client)、一个主控服务器(Master)和多个子表服务器(Tablet Server)。3 p/ b: q$ g! P# Y; h1 I4 n
图 6-2 Bigtable的整体架构% y7 R/ B/ y7 M3 E+ p
●客户端程序库(Client):提供Bigtable到应用程序的接口,应用程序通过客户9 p1 n' i% f: j3 f
端程序库对表格的数据单元进行增、删、查、改等操作。客户端通过Chubby锁服务1 P3 ]/ I% b3 T, a
获取一些控制信息,但所有表格的数据内容都在客户端与子表服务器之间直接传
2 Z9 K$ C2 k5 H; d& ~- d送;8 F" U+ O0 s: s* F2 J" D8 X
●主控服务器(Master):管理所有的子表服务器,包括分配子表给子表服务
5 W* w/ ~0 K7 n v& F器,指导子表服务器实现子表的合并,接受来自子表服务器的子表分裂消息,监控
1 q- L0 M! \/ N0 F1 r( \5 Z子表服务器,在子表服务器之间进行负载均衡并实现子表服务器的故障恢复等。/ f- T/ O. l( H" c
●子表服务器(Tablet Server):实现子表的装载/卸出、表格内容的读和写,子3 }) t# y: b2 J+ `1 u+ c1 U
表的合并和分裂。Tablet Server服务的数据包括操作日志以及每个子表上的sstable数
4 k9 C6 ~$ C; s7 q5 m据,这些数据存储在底层的GFS中。
7 H) C- M, X9 M# ?0 @0 Y+ P T* VBigtable依赖于Chubby锁服务完成如下功能:' p1 S1 o1 z' L/ c
1)选取并保证同一时间内只有一个主控服务器;
7 Q; V S4 J8 h4 i6 j: \2)存储Bigtable系统引导信息;
2 |. H7 }1 d- |: Z8 q* C e% a3)用于配合主控服务器发现子表服务器加入和下线;
9 U0 Y2 P! F( o0 h/ f, b% n4)获取Bigtable表格的schema信息及访问控制信息。* [0 s' }7 h1 P& _
Chubby是一个分布式锁服务,底层的核心算法为Paxos。Paxos算法的实现过程需
8 h. U! Y1 R- v, r要一个“多数派”就某个值达成一致,进而才能得到一个分布式一致性状态。也就是
2 i, k; I$ Q9 [: A说,只要一半以上的节点不发生故障,Chubby就能够正常提供服务。Chubby服务部& G* G1 T% y ~% t5 K j
署在多个数据中心,典型的部署为两地三数据中心五副本,同城的两个数据中心分
$ Q' P8 n/ m: W) O* F$ w别部署两个副本,异地的数据中心部署一个副本,任何一个数据中心整体发生故障
- H" i% R+ H% g: t8 G都不影响正常服务。5 i- f; L2 U: J& S
Bigtable包含三种类型的表格:用户表(User Table)、元数据表(Meta Table) k3 i# p4 W3 q2 ?) Z G/ }; Z" ^
和根表(Root Table)。其中,用户表存储用户实际数据;元数据表存储用户表的元
8 j/ \/ ]3 u/ f数据;如子表位置信息、SSTable及操作日志文件编号、日志回放点等,根表用来存
+ J* w* p8 E5 p& w4 s0 F储元数据表的元数据;根表的元数据,也就是根表的位置信息,又称为Bigtable引导
- H5 C) ^5 k1 R' T信息,存放在Chubby系统中。客户端、主控服务器以及子表服务器执行过程中都需2 m& I1 J; y/ L
要依赖Chubby服务,如果Chubby发生故障,Bigtable系统整体不可用。* ]- ?( g j9 n% a
6.1.2 数据分布
4 \0 Q$ |0 C/ \( ~1 L7 S8 _' vBigtable中的数据在系统中切分为大小100~200MB的子表,所有的数据按照行& w* R z9 M) l5 b3 ~) o* c# j
主键全局排序。Bigtable中包含两级元数据,元数据表及根表。用户表在进行某些操
' U2 C" q+ N/ U9 V4 y! J9 @1 A作,比如子表分裂的时候需要修改元数据表,元数据表的某些操作又需要修改根
5 ~* q9 k4 {* ^# b; D7 j$ ?- N6 N表。通过使用两级元数据,提高了系统能够支持的数据量。假设平均一个子表大小
- ~* q }0 y2 ]" x: n% v为128MB,每个子表的元信息为1KB,那么一级元数据能够支持的数据量为* k. a' r! K3 r ^8 Z# j" X
128MB×(128MB/1KB)=16TB,两级元数据能够支持的数据量为# P; ?* }+ D3 {, ]3 q' p
16TB×(128MB/1KB)=2048 PB,满足几乎所有业务的数据量需求。如图6-3所示。5 n" r6 c5 B$ t) ~* U; M
图 6-3 Bigtable数据结构% c/ c$ Y6 o# o0 K2 x
客户端查询时,首先从Chubby中读取根表的位置,接着从根表读取所需的元数! ~9 }8 I5 o# B. u% V4 O) [" l
据子表的位置,最后就可以从元数据子表中找到待查询的用户子表的位置。为了减) U0 Q" v1 Z, U4 r: I `
少访问开销,客户端使用了缓存(cache)和预取(prefetch)技术。子表的位置信息
2 r9 ^9 X: I! I6 m! D7 J被缓存在客户端,客户端在寻址时首先查找缓存,一旦缓存为空或者缓存信息过- i5 g* O' y- k$ ^1 F- b; C$ j
期,客户端就需要请求子表服务器的上一级元数据表获取位置信息,比如用户子表
R3 f s- v: j& |/ D缓存过期需要请求元数据表,元数据子表缓存过期需要请求根表,根表缓存过期需
: k7 U+ {) q5 m3 I要读取Chubby中的引导信息。如果缓存为空,最多需要三次请求;如果缓存信息过
6 t. B/ }$ ]1 c" r! K2 x期,最多需要六次请求,其中三次用来确定信息是过期的,另外三次获取新的地
& _0 A" x! J' W, x) ~# A4 V) E" V址。预取则是在每次访问元数据表时不仅仅读取所需的子表元数据,而是读取连续) j0 b# X( z# q+ q
的多个子表元数据,这样查找下一个子表时不需要再次访问元数据表。" J- r/ [0 o7 T- s& f5 T" b! J
6.1.3 复制与一致性% E9 g' i: B# Z, Y& @
Bigtable系统保证强一致性,同一个时刻同一个子表只能被一台Tablet Server服
- }* Q6 r: a1 O4 z: e# n务,也就是说,Master将子表分配给某个Tablet Server服务时需要确保没有其他的4 X2 C$ A, N' X$ T* P% L3 T- D
Tablet Server正在服务这个子表。这是通过Chubby的互斥锁机制保证的,Tablet Server
2 _; w4 i& |0 p& {启动时需要获取Chubby互斥锁,当Tablet Server出现故障,Master需要等到Tablet
: }/ I+ E) I7 h/ _- X0 g1 `Server的互斥锁失效,才能把它上面的子表迁移到其他Tablet Server。5 @* z" E( `3 E& X) Z% a5 y
Bigtable的底层存储系统为GFS(参见前面4.1节)。GFS本质上是一个弱一致性
' ?0 B9 m! \/ u( o/ [& i3 _/ Q系统,其一致性模型只保证“同一个记录至少成功写入一次”,但是可能存在重复记
, w' u5 m9 _- H- F( E+ }录,而且可能存在一些补零(padding)记录。
: v% E6 H5 \1 ?* {7 z; gBigtable写入GFS的数据分为两种:( p; t. {4 j$ n( O* z* X5 c; K
●操作日志。当Tablet Server发生故障时,它上面服务的子表会被集群中的其他
8 O( q: |1 v, l- PTablet Server加载继续提供服务。加载子表可能需要回放操作日志,每条操作日志都
: N l9 f4 ]0 ^: Y* c) Q9 F有唯一的序号,通过它可以去除重复的操作日志。
& }# N) b5 t2 C+ e2 l7 B●每个子表包含的SSTable数据。如果写入GFS失败可以重试并产生多条重复记 m6 `" y% p" V5 o
录,但是Bigtable只会索引最后一条写入成功的记录。
1 C6 y8 _/ J; I! x: o+ M0 ABigtable本质上是构建在GFS之上的一层分布式索引,通过它解决了GFS遗留的
9 w+ v3 x% T% h: {一致性问题,大大简化了用户使用。( a- X' J3 S' ^7 y' @) @8 G# _/ ?
6.1.4 容错
3 y6 M5 e5 }9 n/ O; Z, HBigtable中Master对Tablet Server的监控是通过Chubby完成的,Tablet Server在初始6 A- w5 r" v' t$ ~( V! J/ P/ C
化时都会从Chubby中获取一个独占锁。通过这种方式所有的Tablet Server基本信息被3 m' h; H ?( `. s% `6 G, X
保存在Chubby中一个称为服务器目录(Server Directory)的特殊目录之中。Master通
2 Q3 A& m7 K0 {过检测这个目录以随时获取最新的Tablet Server信息,包括目前活跃的Tablet Server,
. _ B% W6 b* j# B5 d1 a! X以及每个Tablet Server上现已分配的子表。对于每个Tablet Server,Master会定期询问其
6 @7 y% L% V, C% e! H1 T* |独占锁的状态。如果Tablet Server的锁丢失或者没有回应,则此时可能有两种情况,
: s" l' `' L/ I) j) N# c要么是Chubby出现了问题,要么是Tablet Server出现了问题。对此Master首先自己尝3 i/ H) D! Z j; r5 h9 j' k
试获取这个独占锁,如果失败说明是Chubby服务出现问题,否则说明是Tablet Server
9 k& C7 Y% R& i% Q. a5 B9 p出现了问题。Master将中止这个Tablet Server并将其上的子表全部迁移到其他Tablet
: k2 H$ T; F& kServer。
7 {" V: @2 s* _9 }6 v) T每个子表持久化的数据包含两个部分:操作日志以及SSTable。Tablet Server发生
: u1 m ]" f+ {故障时某些子表的一些更新操作还在内存中,需要通过回放操作日志来恢复。为了
" n) P: R: [6 v4 d% r4 A% q! k8 k0 E提高性能,Tablet Server没有为它服务的每个子表写一个操作日志文件,而是把所有6 q: _! R7 q4 \; e. _* g: o! l! T
它服务的子表的操作日志混在一起写入GFS,每条日志通过<表格编号,行主键,日
8 I- u) e( f+ ^& v志序列号>来唯一标识。当某个Tablet Server宕机后,Master将该Tablet Server服务的6 q- y- l+ k1 f* {. g3 W' D
子表分配给其他Tablet Server。为了减少Tablet Server从GFS读取的日志数据量,: x- U+ l7 |: F! h1 `- Z
Master将选择一些Tablet Server对日志进行分段排序。排好序后,同一个子表的操作
6 }. l+ T/ B4 n7 \ M; H/ x日志连续存放,Tablet Server恢复某个子表时只需要读取该子表对应的操作日志即" J* g* o* X( K0 G( B) |
可。Master需要尽可能选择负载低的Tablet Server执行排序,并且需要处理排序任务
7 f. O. ^ ]& X) t6 S+ |失败的情况。
0 M6 p1 Q2 E4 ^) p) V# iBigtable Master启动时需要从Chubby中获取一个独占锁,如果Master发生故障,0 [& a4 P, `9 ]* N/ [4 l- K8 {( C
Master的独占锁将过期,管理程序会自动指定一个新的Master服务器,它从Chubby成
2 u' z3 t: m1 t) T. ]" b0 y功获取独占锁后可以继续提供服务。
5 S3 z9 K7 |: N, q/ Z6.1.5 负载均衡
% u. [# {6 T8 g$ @ z子表是Bigtable负载均衡的基本单位。Tablet Server定期向Master汇报状态,当状
' e- C; O6 N! _' K态检测时发现某个Tablet Server上的负载过重时,Master会自动对其进行负载均衡,
7 D% O+ H/ E9 h3 q4 ^4 o- g5 g: v* o7 I即执行子表迁移操作。子表迁移分为两步:第一步请求原有的Tablet Server卸载子
) w8 `: o( J Y, f5 i$ i. i表;第二步选择一台负载较低的Tablet Server加载子表。Master发送命令请求原有的
! E4 O% S- h, nTablet Server卸载子表,原有Tablet Server解除加在子表上的互斥锁,而新的Tablet
# J8 S8 n- B" ?; W7 V ~1 O+ TServer加载子表时需要首先获取互斥锁。如果原有Tablet Server发生故障,新的Tablet
3 g8 _1 N( \& Z( l: F9 {Server需要等待原有Tablet Server加在子表上的互斥锁过期。子表迁移前原有的Tablet
5 c% @% r& {3 i7 \* e5 P+ nServer会对其执行Minor Compaction操作,将内存中的更新操作以SSTable文件的形式; k9 |* @- f' ^4 q
转储到GFS中,因此,负载均衡带来的子表迁移在新的Tablet Server上不需要回放操, g: a* _* s; j3 v
作日志。
9 C" `' ?6 m" ]子表迁移的过程中有短暂的时间需要停服务,为了尽可能减少停服务的时间,
% }# s# U" ~5 bBigtable内部采用两次Minor Compaction的策略。具体操作如下:0 E d3 i& A- M+ V: L
1)原有Tablet Server对子表执行一次Minor Compaction操作,操作过程中仍然允( B3 |2 S1 U& {5 ~5 u$ I
许写操作。
3 P' |) i9 c x! I2)停止子表的写服务,对子表再次执行一次Minor Compaction操作。由于第一
9 \4 f- t& V% {次Minor Compaction过程中写入的数据一般比较少,第二次Minor Compaction的时间
# e1 q, E3 g/ i. S会比较短。
4 G; a; t! v8 {) R6 L5 P" f& o由于Bigtable负载均衡的过程中会停一会读写服务,负载均衡策略不应当过于激: u* z0 M5 }: {9 N/ U; Y
进。负载均衡涉及的因素很多,Tablet Server通过心跳定时将读、写个数、磁盘、内
# F. M+ V/ ~: g) R存负载等信息发送给Master,Master根据负载计算公式计算出需要迁移的子表,然后放
V: f q; {* A4 o6 y/ J入迁移队列中等待执行。
( E4 D' A# h8 Y; _6.1.6 分裂与合并7 w, M0 X$ b* p$ L7 c
随着数据不断写入和删除,某些子表可能太大,某些子表可能太小,需要执行
6 b! n6 i. G8 _3 u子表分裂与合并操作。顺序分布与哈希分布的区别在于哈希分布往往是静态的,而
7 e1 S6 T8 S( R& {) s! W9 O顺序分布是动态的,需要通过分裂与合并操作动态调整。
& ~* t* D& u+ S, a% j9 lBigtable每个子表的数据分为内存中的MemTable和GFS中的多个SSTable,由于
4 x. O& [6 ^0 x- v" _( p$ dBigtable中同一个子表只被一台Tablet Server服务,进行分裂时比较简单。Bigtable上
7 \: n. K: r) Q+ ~执行分裂操作不需要进行实际的数据拷贝工作,只需要将内存中的索引信息分成两
, w# b$ Q; q* b6 F份,比如分裂前子表的范围为(起始主键,结束主键],在内存中将索引分成(起始
" u8 O3 o# j( y2 y主键,分裂主键]和(分裂主键,结束主键]两个范围。例如,某个子表(1,10]的分: {- O, F+ l# R% q/ n% \. c
裂主键为5,那么,分裂后生成的两个子表的数据范围为:(1,5]和(5,10]。分裂
; s) o# E" f; s1 F/ g! a% k1 u5 x以后两个子表各自写不同的MemTable,等到执行Compaction操作时再根据分裂后的9 U! J. i8 L6 d1 N, x3 ]5 o/ A7 z
子表范围生成不同的SSTable,无用的数据自然成为垃圾被回收。
5 u- m1 m* j3 [$ L* Z9 J" q分裂操作由Tablet Server发起,需要修改元数据,即用户表的分裂需要修改元数
* i1 G$ I1 C c9 l5 k7 d$ X据表,元数据表的分裂需要修改根表。分裂操作需要增加一个子表,相当于在元数
, ]# J; f* Z4 s据表中增加一行,通过Bigtable的行事务保证原子性。只要修改元数据表成功,分裂
7 L5 X6 H4 U# _/ C) v9 R操作就算成功。分裂成功后Tablet Server将向Master汇报,如果出现Tablet Server故
: k1 ~6 Y( ~/ C0 m障,Master可能丢失汇报分裂消息。新的Tablet Server加载这个子表时将发现它已经4 e/ b3 h2 G9 l
分裂,并通知Master。; I& c' Q+ j/ U
合并操作由Master发起,相比分裂操作要更加复杂。由于待合并的两个子表可能
0 l9 b3 a8 s- P( ~被不同的Tablet Server加载,合并的第一步需要迁移其中一个子表,以使它们在同一/ p5 F$ l, {' f$ j% Y1 G" Z; [- _+ `
个Tablet Server上,接着通知Tablet Server执行子表合并。子表合并操作具体实现时非2 \9 F- a/ L" z5 k
常麻烦,有兴趣的读者可以自行思考。
Q, R- q9 D( E1 y4 v$ s' a! g0 ]! F6.1.7 单机存储/ o0 L' y+ D3 a, P, f) k
如图6-4所示,Bigtable采用Merge-dump存储引擎。数据写入时需要先写操作日
6 J. d+ @5 c* f! B1 F8 D) W8 K* i志,成功后应用到内存中的MemTable中,写操作日志是往磁盘中的日志文件追加数" i6 v" z( |/ w' T# V3 q
据,很好地利用了磁盘设备的特性。当内存中的MemTable达到一定大小,需要将) c+ J2 ^( H) s& L+ M: K& `, r, e& C' p
MemTable转储(Dump)到磁盘中生成SSTable文件。由于数据同时存在MemTable和) W% a# c K8 G! A& B+ R T
可能多个SSTable中,读取操作需要按从旧到新的时间顺序合并SSTable和内存中的
/ z9 a4 V( ^$ q+ h/ m0 rMemTable数据。数据在SSTable中连续存放,因此可以同时满足随机读取和顺序读取
" P" D# X- O: K# i两种需求。为了防止磁盘中的SSTable文件过多,需要定时将多个SSTable通过
9 G9 o# h. ]- ]# i* Rcompaction过程合并为一个SSTable,从而减少后续读操作需要读取的文件个数。一
3 C. h* S" ?/ I& ^7 U- _- J( f8 Z$ r般情况下,如果写操作比较少,我们总是能够使得对每一份数据同时只存在一个9 u/ B' f4 F: e# I z9 U- p
SSTable和一个MemTable,也就是说,随机读取和顺序读取都只需要访问一次磁盘,
6 d7 |. g! r0 ^3 {1 c y4 B' t这对于线上服务基本上都是成立的。: l* g+ `! i# w5 s" D9 [1 d2 u
图 6-4 Bigtable单机存储引擎
4 t+ v4 r) J* I; w- |# m' @插入、删除、更新、增加(Add)等操作在Merge-dump引擎中都看成一回事,除
# l) V- k1 y, E% m; v$ D了最早生成的SSTable外,SSTable中记录的只是操作,而不是最终的结果,需要等到
0 f8 v9 U0 a* s* b8 U) M& @; s. @读取(随机或者顺序)时才合并得到最终结果。6 ?( p3 G* g1 l, N+ _/ M9 J+ A
Bigtable中包含三种Compaction策略:Minor Compaction、Merging Compaction和
# t2 ^$ N' I7 s6 [Major Compaction。其中,Minor Compaction把内存中的MemTable转储到GFS中,9 p9 H5 s+ D7 u" P0 H( U
Merging Compaction和Major Compaction合并GFS中的多个SSTable文件生成一个更大
0 V4 w- u; N; z2 P& B1 v的SSTable。Minor Compaction主要是为了防止内存占用过多,Merging和Major
4 ~# q' z( U; c: x. i) \' NCompaction则是为了防止读取文件个数过多。Major Compaction与Merging Compaction
# p2 @' M. \- C- A的区别在于Major Compaction会合并所有的SSTable文件和内存中的MemTable,生成5 s6 j1 J! r& h5 I
最终结果;而Merging Compaction生成的SSTable文件可能包含一些操作,比如删除、9 A( a7 S. c* W. T6 F( d% b" o
增加等。
' U: }5 g2 S5 _7 @数据在SSTable中按照主键有序存储,每个SSTable由若干个大小相近的数据块
1 @6 Q* g, V. K. L3 a6 R' s(Block)组成,每个数据块包含若干行。数据块的大小一般在8~64KB之间,允许( [+ Q, r3 u7 ?7 d M& ^/ D
用户配置。Tablet Server的缓存包括两种:块缓存(Block Cache)和行缓存(Row, A, a+ K) l3 P4 f3 R' a' `, W
Cache)。其中,块缓存的单位为SSTable中的数据块,行缓存的单位为一行记录。随
3 }0 |: k- ?, I. u6 e机读取时,首先查找行缓存;如果行缓存不命中,接着再查找块缓存。另外,$ N: [ `/ @) x+ t- P1 X
Bigtable还支持布隆过滤器(Bloom Filter),如果读取的数据行在SSTable中不存在,4 k' s" B* h+ U) A+ o/ T( d$ |
可以通过布隆过滤器发现,从而避免一次读取GFS文件操作。
4 s- a0 v* l! m6 k1 Y, g9 Z6.1.8 垃圾回收
* i* `7 O" R! JCompaction后生成新的SSTable,原有的SSTable成为垃圾需要被回收掉。每个子
; p& Q1 T4 V: t2 G9 n表正在引用的SSTable文件保存在元数据中。Master定期执行垃圾回收任务,这是一
7 Z x9 ?* ?6 J0 l, q个标记删除(mark-and-sweep)过程。首先扫描GFS获取所有的SSTable文件,接着扫$ ]( s' ?& i' f2 c1 f
描根表和元数据表获取所有正在使用的SSTable文件,如果GFS中的SSTable没被任何
3 ?& s' Q( d/ Z; Z' D4 a0 P j; Z2 K+ T一个子表使用,说明可以被回收掉。这里需要注意,由于Tablet Server执行
2 w E ^2 x' y0 e7 F3 G& ZCompaction操作生成一个全新的SSTable与修改元数据这两个操作不是原子的,垃圾5 ^9 F* R( M/ u7 M+ |; d3 Y" K
回收需要避免删除刚刚生成但还没有记录到元数据中的SSTable文件。一种比较简单
% B9 B) a, M8 ?& K6 R2 R. M的做法是垃圾回收只删除至少一段时间,比如1小时没有被使用的SSTable文件。" K! r: l( P0 ]8 T: w2 ] ~- l+ d! d
6.1.9 讨论) t5 J, n, I6 y% W$ s# E
GFS+Bigtable两层架构以一种很优雅的方式兼顾系统的强一致性和可用性。底层, w1 n4 u+ \( |0 s, `9 E! q
文件系统GFS是弱一致性系统,可用性和性能很好,但是多客户端追加可能出现重复# ~- f. T8 |" x$ A) Z
记录等数据不一致问题;上层的表格系统Bigtable通过多级分布式索引的方式使得系
9 N% X* \, a/ c/ t% Z8 @) O统对外整体表现为强一致性。Bigtable最大的优势在于线性可扩展,单台机器出现故4 \# _; o9 |1 z
障可将服务迅速(一分钟以内)迁移到整个集群。Bigtable架构最多可支持几千台的. `0 K2 t7 ~% l7 u. |* N
集群规模,通过自动化容错技术大大降低了存储成本。% k; i/ ^; v; E$ a
Bigtable架构也面临一些问题,如下所示:
& I n! |9 c3 U6 N; J- O●单副本服务。Bigtable架构非常适合离线或者半线上应用,然而,Tablet Server
+ z" ~+ G% ]9 \8 n- Z$ f% d节点出现故障时部分数据短时间内无法提供读写服务,不适合实时性要求特别高的
% x: m5 D/ Q# y; K业务,如交易类业务。2 H1 Z. m, n* v3 {( V
●SSD使用。Google整体架构的设计理念为通过廉价机器构建自动容错的大集; p! a1 _6 M# W- k
群,然而,随着SSD等硬件技术的发展,机器宕机的概率变得更小,SSD和SAS混合
# v* h. J1 u' _. B+ m存储也变得比较常见,存储和服务分离的架构有些不太适应。* B2 x5 Y, k L1 m
●架构的复杂性导致Bug定位很难。Bigtable依赖GFS和Chubby,这些依赖系统本
' u4 h: Q) O5 E% @& l身比较复杂,另外,Bigtable多级分布式索引和容错等机制内部实现都非常复杂,工1 i5 x! [4 L' i) V) a# }( C
程量巨大,使用的过程中如果发现问题很难定位。
6 \+ }1 J) d& G" A+ r2 g+ t" T& C; _总体上看,Bigtable架构把可扩展性和成本做到了极致,但在线实时服务能力有. ?7 o3 y$ w8 O) x+ E
一定的改进空间,适合通用的离线和半线上应用场合。 y! `- |1 T+ K2 Q# B& F
& m# N, r2 F/ o
/ `) a" m& f- }0 @+ g L |
|