|
|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。
/ j7 G5 C2 H( l5 I
# Q. {3 G/ e* L# F/ u* U2 Q 阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?3 y+ e4 T& ^3 m3 J) `4 V6 g. A
$ U! n' }( R0 Q- u5 d
不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。8 r2 _& u# {( G
" h' T4 N% m/ g; k4 m
; B) H; e( o4 e0 j& Z
- h* \1 F S1 g# H4 o图1-1 MongoDB架构图 2 L2 `- ]1 b; M0 U
* \, w+ [- f4 b4 H+ L MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。' H( K( W3 B) K
$ P, ^- b# d2 Z& w5 G/ _% E0 pShards
3 N; S3 s% a5 ^
9 G. Z6 ~) P9 K+ W MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。! r3 H/ O7 U8 [9 N! Z
: q) g/ t4 ?( m3 {2 x9 M
Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。
* W- t3 X2 a$ `+ b3 t6 {
# J. W/ U( W9 v. I 每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。( [. O' W7 M2 K0 G+ g" f/ g* ]
5 x9 h( O" U u0 Q 如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。; f, |$ \6 {% Q! Y& N3 Q
3 l' @- D. a$ b# ~: E' K
Shard keys
: ?' K% i& P% l$ _ ( U; \7 t9 H3 X" L) e$ I2 `
为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。* c w/ j0 O& k; [7 y* }
% R# x2 f1 {% h0 f- w1 Z% T 如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,' ~; Q, a% m# y; }5 y" l" U# H
! `" z5 W) N b+ H
{& [* h3 `1 n7 L$ q' u
"ISBN": "987-30-3652-5130-82",. W* p8 ^ w/ T9 e
"Type": "CD",1 Y) Z- n. b4 K( D3 K" i; `
"Author": "Nirvana",
& ^. m* }& ?* E; K. b "Title": "Nevermind",
: C- V. ~& r5 S+ Q2 P- { "Genre": "Grunge",- s3 s$ L4 h! j! |9 |
"Releasedate": "1991.09.24",: M4 J, a) x; J6 h
"Tracklist": [' s4 F, `/ k% k- A3 O) y5 l
{# }" X) C* b$ P
"Track" : "1",/ J" U# d+ P8 J _7 _, I" F; g
"Title" : "Smells like teen spirit",& T: F7 |0 l: z
"Length" : "5:02"" z. C" D' I7 X# W9 h$ B) N
},' W0 V- Z& l) f+ n5 r, A
{
6 r3 f. D9 f: R/ R& W: j: L. x "Track" : "2",
8 o4 H3 _! n+ Z) S+ a "Title" : "In Bloom",6 l# H4 A H4 r& k+ B6 D
"Length" : "4:15"
4 ^) A* l6 G$ H8 Z8 a, S# @0 T }
6 V6 g9 W' N, d, S4 M1 } ]
+ P7 _; K" k' N9 n5 ?# L) I}
! r0 r! b |& l8 L/ h3 d
, Y3 Y: G7 q! q{
& M u8 ~+ T8 I$ d& J6 J& n "ISBN": "987-1-4302-3051-9",
$ k9 W3 U, D# A) {0 d7 R" O0 R "Type": "Book",. M' W9 y9 w4 F6 w9 C
"Title": "Definite Guide to MongoDB: The NoSQL Database",9 X Q2 c. w' r. \- k' W
"Publisher": "Apress",
% K' m- @% L: H6 K: C& z$ | "Author": " Eelco Plugge",
) S* `& F' n- V. c "Releasedate": "2011.06.09"
1 ~! U) q8 i6 f/ L9 K! W7 j}% S, C4 W0 y: A; Q- U' P9 S2 ]
; C3 D& X; \* t
假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。
0 \; U7 h. D" H$ T5 w$ j f+ s4 e2 {' i$ B$ \& _. q& }
在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。
: U6 U% u: w) r/ c' p. p4 F1 I9 T2 f4 H/ ?% S. j& W
例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。
; g; @* _4 `# {6 u3 g" C8 G3 c" {* z# `% _; ~
很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。
# Q+ H8 f) n$ F# U; h) M3 L# x3 B6 ^
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。# T! z. h# e4 ?# @: S3 z
+ A& R) J- [" w. N+ SChunks
/ O) a+ ~* U3 [3 A1 p8 d6 q
; {$ B$ i; j( a+ B" Z0 o MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。
2 O! @, A0 _6 }2 K7 k& \: V/ L
! u, A2 a: j/ F( h
! `0 [; A- ^0 w2 {& V/ g
图1-2 chunk的三元组
* c1 z- W i8 Q7 C- s, u$ F0 z& ~/ S6 j& B7 Z# ]9 X1 r1 |8 K
其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。
8 P7 t: y4 o5 [5 C
9 O3 M$ R( f, ~: K7 Z$ k& C ^ 如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。
P( b; [& ^' k
3 H& P. m& t2 l; \1 x. k& Y( D Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。5 y U! N9 r' |& R+ v* N# C+ I/ d8 i
9 _ X Z6 e# d2 \6 d- ?
一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。4 `" c4 j. m% _ ?* `! d
6 A; n) l1 Z$ F5 i+ ~& f; L% R0 x6 r 当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。
4 r% T% v3 F2 @' ~8 t- K, h' `: A5 H# d1 w
这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。: _' L% J! _3 L% Y6 _, A1 B
5 o; F( c5 ]" I! A. R7 BReplica set+ {2 q: F3 L0 D O; A5 K
3 o6 A0 i) S# c2 A0 w K 在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
% h B2 r4 K4 a# {& ]# S! ? K' y$ G/ }7 L+ l* r+ n) C
这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。5 k9 n& c$ q* n5 h+ R1 A. q' `
! k) E4 w. c. R7 [8 _. n) m# E: i Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。
6 i. a, {5 H9 A! M
* J/ y, B0 h7 i8 T' t2 h8 _$ c Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。
4 n6 Y7 ?$ s5 `
6 \8 d/ C! P1 G4 {$ G# H7 f1 C3 SConfig Server& Q2 s; d) D6 \: u( h' ^; \' m0 b
6 s3 b& W: e! I0 _
Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。
& {* p, f6 Z: g; j1 l% J) O$ y* B& Q* e9 }# _
每一个config server都包括了MongoDB中所有chunk的信息。
( H, t4 Z* [3 H( p9 d
$ B% i; [ |" f" M* Q1 S* g% l, w Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。1 {2 A9 ~# G8 D6 t- Y( g
& a- J5 |! W! n% C) b0 \
如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。, p5 f( n. I! v+ V2 D" X6 Z
& K0 ]4 |4 T: `3 b5 n0 \0 O- {0 m: P MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。" J) e- C+ O' f" }. h# |8 Z. G4 K
+ [) x1 b5 R& ~1 U8 e' MMongos
- f/ H$ U, j ]. w* a- g8 i& ?0 D5 m- \7 \
用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。
5 P8 c- L O. {( {& d2 M' A! s
! N w: }& S( n6 j 当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。: H. B4 e' @8 H0 P
0 G! u" _1 ^& h! J/ J/ A( A
Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。
) T2 u# {$ y0 H$ p( m+ @$ P6 L* t+ h
Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。
' ^4 t3 m$ N; y, k: g
4 q8 }7 n" ^5 X 通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。
, ^( X0 |8 G% Z, v* Q2 @& H2 ` U, k% s$ P, v: s0 X# u& f
8 H A6 V6 e0 O9 f: I
Reference,
% R) }: [2 b8 Y8 t) \9 J5 o' \) ` H% I
[0] Architectural Overview3 j; |% e4 o( e2 x8 e g
http://www.mongodb.org/display/DOCS/Sharding+Introduction5 t) O1 v8 L7 B
|
评分
-
查看全部评分
|