|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。& b. G9 O/ {# g* c" u! K
! ]7 r1 g$ }! C) a h% J$ q) H
阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?% w7 r6 T7 c( b( t
: _! Y$ B/ h, {7 \& Z1 @) M 不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。
' T! E. n% j6 j+ s/ S! d1 h+ y ?+ p& Q s) q
+ y0 y$ u# K$ l5 {8 ~, I
% k$ B( G1 n8 l% V: @8 d% h7 M图1-1 MongoDB架构图 ; q- T2 j: {+ W* |# K* }
9 T9 ~5 G% \, ? MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。
: t9 H- p1 Y) h+ x: d7 H" i! Z" `, ]* e+ \
Shards
7 s' v$ E& m9 K9 c! j# k5 _( ]
) z& P& \! Q: s( R7 J5 L$ X8 L MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。: u5 ?1 [# c- r& p2 e4 n
6 I' s2 L" A g0 c
Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。
+ U* d! v1 g' l$ t+ \+ h- e: ?$ n) g* ^- P% |! H5 k
每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。
( t1 [! |* ? _" B) x, t! m& i2 M# m1 N4 p5 i- w+ f% x
如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
' V- @2 A+ {9 ^5 L; j2 x) C7 X
2 Y$ R: q; P' d/ Q& B' x9 o0 ~Shard keys" p' C+ e* t& p
) w P( f/ M4 M# e" r9 L; [7 I
为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。; e+ n5 O1 v4 k- N# x4 O
6 b0 r0 L5 R( ~( D4 b 如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,
. K9 A0 e: ], d# H5 {6 I
* b3 {, c! }$ D! Z{
' l1 ~- B1 l- s5 c o "ISBN": "987-30-3652-5130-82",& r6 }5 n& R: t ~% x- u% W+ f$ D* x0 g
"Type": "CD",
" f6 B+ n0 d/ \5 j- l "Author": "Nirvana",0 D0 u$ C; M0 L; Q& g- g! d- `+ B
"Title": "Nevermind",
+ X3 J7 w, j5 |: w+ { "Genre": "Grunge",
+ U2 q4 \. v# `" Q$ ~' { "Releasedate": "1991.09.24",
# ^. ~! h: `; o; Q( A+ @9 u "Tracklist": [
+ S( w# w4 b! a- ?! i' V {
# y; h3 P) g5 n- n0 x( Z5 \ "Track" : "1",
x" b* J9 M- D+ P" N) K "Title" : "Smells like teen spirit",
3 v7 u- p' B& c9 M "Length" : "5:02"
6 _* P+ F" s! P# N1 `8 J# _' b },5 K; ~, p" R: u- a. Q( ~5 b7 ~
{
3 `4 e- t2 s, h+ Z q/ B$ i "Track" : "2",
$ x5 x* g+ j; b2 x9 `7 P( ~4 [ "Title" : "In Bloom",
) C; r2 u3 M$ G "Length" : "4:15"9 `$ B) E$ Z6 P1 M8 I: ]) p% e
}' Q. W: S. F7 }' {6 h
]
/ X" H% h! @5 B, h+ b1 F8 k. k}3 \/ T5 M8 w8 h! Z2 H( V8 s
* Z% W3 q$ Q: }: d$ t: Y/ ?6 K
{
/ A5 L. _+ {" ~: S. P ]5 g% `+ c "ISBN": "987-1-4302-3051-9",
- b' ]+ O' A6 `" H" V# d "Type": "Book",
- K+ M8 _, Z$ ] "Title": "Definite Guide to MongoDB: The NoSQL Database",; f# G. e5 f7 |& h
"Publisher": "Apress",+ c9 {; a' O2 U
"Author": " Eelco Plugge",
# b+ D& `* Q9 H5 h "Releasedate": "2011.06.09"
4 }( \* |0 D3 G# z2 e# q; n8 e+ a}
4 G; n, A" Y5 I* B- N0 q3 i3 o7 x+ m$ I/ J: ]4 }
假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。/ f, ~& I5 x! j
9 [$ Z' @% k. B- [9 t: s 在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。% ?$ Z* K% A' T
6 q, v& Q5 L* D9 l* o; t" v 例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。
9 q# A/ [) w" ^7 ^
: }0 ?7 I# P* [# h6 m 很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。
5 z0 e& l" M/ A: A5 m; n2 @1 A1 C. [3 n; C* S
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。* _3 G5 h- o$ f7 u* Y/ [* G
$ c& e$ ?% s+ Y& ~' T% e2 sChunks) J% u1 b8 j: w0 r8 c! A
2 j" y; [, z) E MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。
2 E) g( }% j \! c+ r; u: H8 x: q9 l- _7 U, g
0 z4 {- g( W6 e+ m7 ~* [
图1-2 chunk的三元组
' c0 S, u& h5 M" ]3 D. f0 L: x$ g1 D: s# D! U; R3 x- ^
其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。
+ C/ A% V" ~4 v' W1 h! p* S9 Q# L+ b( M
如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。
- n* m" w* X1 q6 K( \) R5 o
" ]- O$ x! c. r1 q/ o# i( i Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。. D( @% O& ^0 d, `3 y. Y- Q% Z ?9 V7 |
; k& ^5 p3 m, F; @% P+ d. r$ s" \ A
一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。$ O @* n( t6 c, W' B
7 L# b) ~& t7 A+ @; C% r3 p. ]
当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。* ^0 t6 j: y: W
# y1 G4 j+ _ d* R% p0 b3 `9 N
这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。
& B" }& @6 s5 S0 K. s; c7 ~$ I) u' o" ^
Replica set
, J- @/ o9 y, f) p& I M 5 b, Q, F9 a+ L9 S1 w8 b3 n
在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。( ~& { N8 K7 F# I6 N
4 Y6 ]7 \. e5 b
这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。
6 `( \+ z& c& N; h+ }% l }& @% e0 C! l1 \( {0 _
Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。/ j) s9 l0 [3 v% y. Y8 _( ]
5 C& |7 [% `7 H0 A4 |- \ Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。
9 l$ O; e' c j7 @* m7 `0 I1 c9 j$ r& b0 e. k3 x
Config Server
( m" q7 [6 U4 h4 { / I) w1 l- q B2 f0 ~* Q2 T/ p4 N2 Y9 J
Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。
' E6 Q0 I3 D F9 r- h
u) Y8 W: S/ h' {+ X2 \, } 每一个config server都包括了MongoDB中所有chunk的信息。, `8 `3 F; W& [" G5 U* W' p/ y/ i! j
: | f, n9 {: i- t6 d6 w Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。$ A# ~6 W' I" A( w1 b/ Y# J
/ s" _5 h! [0 a: Q& ^+ { 如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。# K' J4 W5 N: j* o0 R* d
& c9 X% l o7 F
MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。- h# k3 o, M: X9 {
4 c+ D: Y7 a* W- h* u( eMongos. n; H, y$ X9 B4 n' |6 c
5 U Y! X) j! c' p0 F 用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。
5 l1 P7 [5 W1 f+ Q! _- Z' d5 ~
当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。
+ p$ O2 `1 h$ N2 ?/ @. i5 J
) |6 @# k7 t4 c( m Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。/ X3 K; N( O3 G$ z& E% y
: V# P, f g! D7 R; M5 j Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。# T& d0 Y6 |# |; B3 i* `
2 T0 P. q$ M- ?9 {7 U9 r- z 通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。
7 V0 y3 o6 B" R0 Y1 \7 \/ q
, F6 u: t) A" H# O' t7 x, t- t& ?; J5 u) W
Reference,2 n4 z$ y [( x, Y. X# t
! d( \' a& B4 \5 I[0] Architectural Overview
# X( }; l7 R5 W7 B( Lhttp://www.mongodb.org/display/DOCS/Sharding+Introduction. j6 i+ o4 `- S* T9 O+ e+ X
|
评分
-
查看全部评分
|