|
|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。* O' z* a1 Y: F* N
- s' w& R N: h" O% H" I4 z% ?% t
阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确? P. O7 A# X* q# o
1 `9 H( b( L) V* K/ j1 x# M 不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。
4 q5 w+ D6 S t! ?/ _/ q2 v w. f) d `
- K' Y2 [ E3 _/ @( x6 o+ Z6 o5 @- x. ^$ p+ Y9 e
图1-1 MongoDB架构图
" w5 x- t) A* e% ]8 h, a$ v
) a( _# f" [6 @' {& P MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。6 ~4 l/ A! s& J8 r1 w
7 O N* n4 c$ S+ p7 eShards4 V6 b' Z1 {: b6 f+ u4 Q$ \8 W
2 E( ]* j& V5 S3 t; I7 Y MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。; x( K7 m* z4 j8 A6 C- P4 K6 ~9 n; F: ~$ a3 o
: r* K/ X* _7 l( @+ ~ Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。2 I9 Z& |2 I5 R( J
7 }) F A9 d; Z9 ^0 r" s
每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。
( W H/ i5 x9 s/ N
' ^' v1 h$ ?4 ^2 [* x 如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
% {! o8 W" Y6 f( \% ~: h" N0 l# r! O6 c- y5 N( Q
Shard keys" I' A0 A/ o) B+ Y' H% `
5 K6 ~ N! d. J' @7 ^ C- o 为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。
. M" H q+ k- N% O2 z' R6 c- @ j& B
如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,/ O/ z* r1 f. D6 T1 V9 s) t. I
8 [% B2 L8 a0 ?7 z; h0 i& L{ e" M t: ?) W
"ISBN": "987-30-3652-5130-82",
- {$ \6 v6 u2 Y5 |1 i% s6 ?' z( Z "Type": "CD",$ J) r" o3 J+ }/ k! X
"Author": "Nirvana",* Q- R; Y' a* D0 }- H! R
"Title": "Nevermind",& g* F0 ^* h8 K* A) l# K
"Genre": "Grunge",
: t+ B$ b! [: s) C! i "Releasedate": "1991.09.24",
* E- t: I; S6 J7 ^ "Tracklist": [
" Q' `+ Y- e( Z( t. l {
6 V7 G! S: t# |, l; c0 h0 _ "Track" : "1",0 ?5 I4 l0 P5 h9 m& q7 A
"Title" : "Smells like teen spirit",
' {7 }, z D, H! C0 x- F& | "Length" : "5:02"
# r$ N2 G7 Z, S2 U1 e5 D' a },6 C7 M9 A3 s+ m9 @% y3 i4 O4 T
{: h+ S4 G! u1 `4 w6 V
"Track" : "2",' H2 r7 P' {/ w+ r$ U
"Title" : "In Bloom",
& K( i, o5 Y8 ? o$ H "Length" : "4:15"
* V) Y* e; f. s3 p+ w# r }
; q& {1 S1 H! R% D ]* H; D1 L# a) @3 T9 t
}
4 k5 C2 s% I8 ]7 D$ V) n+ _
' p2 l6 u2 a7 r4 B& D' \9 t) K( q{
. K: y- V& U% n9 I% F6 i2 [! w; q "ISBN": "987-1-4302-3051-9",
; s( T/ D5 ^" o+ r+ ^* u# U; \ "Type": "Book",' o: }, N7 t0 a+ w8 m/ k# G% [0 v
"Title": "Definite Guide to MongoDB: The NoSQL Database",4 l" k) Q4 g* M
"Publisher": "Apress",
4 k( J6 S, _- B H; | "Author": " Eelco Plugge",7 ?! C$ F D# R" ^
"Releasedate": "2011.06.09"
5 l z+ l# [7 @8 W}* s. M3 ?! ?. ^* j+ \: O
, {- \& s1 }3 s& X r 假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。
. B4 L1 ?2 B$ L
! A# ]( f' f% M2 |# U( d 在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。 O, s- y" ~- V
}/ r- F/ ^, k9 N1 q4 @: ?
例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。. s8 `; _, U/ q, T: X
* a* \7 y! ^" E' g$ `+ f- r
很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。) K. G4 j+ g6 O: t5 k: ^% ?% b
8 {# b* s4 B+ y( _. b
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。2 P6 h* o2 x. F$ [7 M6 {
* E+ Z7 m% G) s5 i% a4 y& w+ X9 e k" p
Chunks) n( v( o" M* k1 P& `4 Q# A
4 Q) p* b6 y0 F
MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。
; w4 v" ?. k$ i: @; Q
9 W& o/ j0 E+ r; L3 F7 u+ B; Y
& m; Z' d4 i m9 G图1-2 chunk的三元组 * z* t2 S* l Q
0 Z. O% O, ]: E" M8 o6 `$ Z5 m 其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。" i/ [6 ~7 S+ M5 g+ \9 J
+ U: [$ b; p* `) ~+ X
如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。& P& H5 l6 T# x9 k, E
, M; z; z) Q8 M2 q( s Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。4 U# N+ w B6 ?, L
_! @( k% v8 ~ 一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。3 v! R! ]" t: {* D6 I
/ R& S" \6 {# z) d9 c$ r. F 当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。 b4 a. S8 i3 V" P+ W0 g
& k* |: s4 G: i 这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。
; j8 a, |+ ?9 j. U" u* v/ v6 F" n9 J8 C
Replica set# y$ [+ l) n2 f0 U2 V; x8 _0 _
7 y a# w) \% ^! r! r5 O6 a" @7 N 在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
$ [1 w* a! y, W6 ~1 A( t& F
' Q% `. l3 V8 H 这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。2 [; o- b0 C% q& B' c( N/ A" a' L- U3 h
" y* ?+ F( t1 V3 B6 V6 ~
Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。
3 w4 {0 C4 S9 E, w1 M% [! y+ _: w# P0 S6 E
Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。( \2 F( J1 E/ `3 f
E$ |: X- j: a$ e4 i
Config Server- ~$ T# {) z) e" U2 @2 K
2 m2 G5 h, o4 L( P+ h1 b Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。. k; L! h& s" E! {# f8 G: c6 r# m, c
% M8 o, t: O: O# W9 k" p5 ^ 每一个config server都包括了MongoDB中所有chunk的信息。
2 z& W# x& w; ]( V, z: a% w# c4 G: C! W7 n3 Y% R
Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。/ r( j1 q8 [" a& Q( D
" C6 M6 l# B; k! Z6 e
如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。* R. d, T( r6 ]& F! r
' m# y. C% `' V$ k6 j/ H& |8 U0 K
MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。
3 q( o* Y$ i. ?1 K/ w. Z8 |+ n/ H/ y* N; h4 I) w( A
Mongos+ S+ A- W. q# I0 R' ]
$ v1 G7 P! x' `' V- q, b 用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。$ A! E0 h" U3 f; F1 P0 W
+ ~- i/ j% d0 Z. c. {- D) k 当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。
" r" o* j6 Q4 [- U' J" i5 U9 `) r8 P# g( E) K/ B6 Z
Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。$ k' _5 U" [% I+ w t: L
6 Q" `8 G: ~- i/ ?
Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。1 }( T/ B: [9 U$ V& s3 I2 z
) [5 {5 \, m! F" Q! @9 z: m+ G 通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。" W7 E1 n9 y1 l3 l2 {) X7 D
: {/ \0 S* U- T, \0 p8 f' Q
2 t! f g L# y% _! k5 gReference,
/ n' H7 H# w; E) w3 v T# G
/ G6 m' k7 L/ _0 V: L2 K- U( z3 j[0] Architectural Overview
* e6 l% J/ G2 O& Q8 H9 I/ w! Rhttp://www.mongodb.org/display/DOCS/Sharding+Introduction" m1 F( T4 _0 G0 m, Z! k6 u4 d
|
评分
-
查看全部评分
|