|
|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。
7 n+ x6 {, U* E" |8 R; ^
. x: E7 _0 e: i" o# W* y, U5 N 阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?
7 |+ ?8 s$ k5 v% s5 Y* G' k7 @! g u1 G! n' |
不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。. C3 K& _% Z" p# S
+ a) e2 M9 \; L8 g. p; P% b0 Z
% r7 T( ?0 S3 X# Q3 H0 u
- L5 Y9 O4 o3 W8 N+ U: e, z ?% A4 B图1-1 MongoDB架构图 # K% H6 K# @7 Y8 X
! E3 i9 @) P2 m @6 b
MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。4 p5 l, u# i1 m |. g2 Q' a
- x2 G1 H! w) g! x3 z: s% F. UShards' @- l* r5 Q6 H- T0 @% {
1 m9 |3 C6 H5 ]2 e( C: ? MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。7 n) }7 W3 F- C. J# w( ]1 z
2 q9 I. p! z- g* r" y Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。
3 T8 T/ [* F0 v% L9 R P# l" A; z6 O8 [6 U
每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。
0 P. [0 o8 U9 e2 a7 {) {" _+ G/ ^1 y
如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
7 n a* ^* V8 u
4 l" a1 H0 c1 s3 r5 f% LShard keys
) N0 d( x+ k% j0 e& V
+ i" a' ?3 {, b* P 为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。
; t5 E6 t4 s7 v, c3 @' m
# n4 L8 R. T, R 如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,
1 K [8 ~7 o* j3 T5 t4 O! C8 M
- c$ w& I2 t! [4 ]( m9 Y/ X& r7 E{4 {* r1 R! l+ {& x
"ISBN": "987-30-3652-5130-82",' y4 {9 F: S3 _2 @9 i3 R+ f" q
"Type": "CD",
1 F: p8 \; V. L; x/ T7 ?6 }! T "Author": "Nirvana",7 X: `, a' w7 @' Y$ K8 r+ C
"Title": "Nevermind",
% I. R& A, A3 z E5 l! i; [ "Genre": "Grunge",
8 _' ~- Y! C2 ? "Releasedate": "1991.09.24",4 [: a4 B0 Z( s
"Tracklist": [$ a7 \# U# _* |: M( N' \* J
{
7 s H5 D9 B5 N" Q/ F2 d "Track" : "1",4 A& n1 d! g4 W* y& l0 S! s' K
"Title" : "Smells like teen spirit",+ g5 R8 _6 T: I. Z
"Length" : "5:02"
( ?5 g! X/ A# D9 O" y% A: K },
! @# |& I# {$ }8 G8 \ {6 }! `3 t! e; M% C7 q
"Track" : "2",# { @/ |$ b/ N) R
"Title" : "In Bloom",
6 M! j" j/ \/ R! F' {, e "Length" : "4:15"/ T8 L" G% N" ]( i$ e! T
}
0 b. S+ L; m4 [' Z. C. l8 u1 i ]" {8 W+ C* R3 I0 \2 v
} a. Z# H9 H! ^1 x, o0 d
5 H% @/ W+ \: H/ y
{
. M/ e" Z# w5 i7 n% s" u "ISBN": "987-1-4302-3051-9",
" x& K, u9 ]/ T1 C3 R4 b; A- y "Type": "Book",
, R; X6 Q1 Q; U3 G "Title": "Definite Guide to MongoDB: The NoSQL Database"," e1 |3 M8 V* ?: f( h
"Publisher": "Apress",* V6 j. N+ c( D8 w9 G) B
"Author": " Eelco Plugge",
2 ?$ L# U( x8 c9 Y4 |. J "Releasedate": "2011.06.09"7 J; X# L3 e, ]8 y7 S
}' u9 x5 Y) J `0 W: S
' Y w1 B: q$ P" V. V( C0 a6 ` u- Z 假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。
: B2 X2 l7 ?0 @, q
2 X( {- j, H+ D p: G% M 在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。
, `; h- T7 ]5 j# h6 K
# M9 o% f+ @( b# m3 @' E 例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。. w9 I9 x, d* ?- k' D5 k2 S5 m
% ^' |' v) w6 G2 N5 ^; e! y
很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。" A. X) D8 f1 I( q) ]
n6 b! T: Y2 K" G' Z$ I0 k 延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。6 B6 V! _( b9 D; R d6 y7 a8 s
6 |$ A( z) ~2 F# ^3 K
Chunks0 i0 s5 ^5 E4 P$ n2 l
; }. p5 w8 M) @+ s: u MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。1 E9 L1 o7 D4 Q% {( Y4 m5 P, D/ R
# a; W s6 j/ C7 `
1 e+ I8 w' p3 h+ f图1-2 chunk的三元组
. |% }2 x, h/ Z0 i" q8 ~# ~ l
3 Y0 h/ ^ Y# U/ F2 t" V 其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。; d; Y: _4 m2 s, H2 K2 c
4 i0 T' n m7 X/ b" H" I
如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。
1 [8 A/ Q5 F# p/ _5 Z; ?
) y: r# \4 ?3 B Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。
: Y n0 B5 ?) n7 n& @. S+ `$ o
7 V% ` L5 V6 O& j+ W9 o 一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。
- }+ Q7 `1 c! @4 x, g @2 k5 E( b7 I+ U0 R- y$ h- C" M7 \
当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。
/ _7 r7 g& I' t! n! L
1 }* X: {1 M! y# X2 q! x, O7 n: f 这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。/ U% z6 T9 i* p5 L' f3 _5 v- w0 @
8 a- K, j" N; H8 p5 H
Replica set
2 N f! x8 w' n7 F+ f " a5 E, N* e( t; { r) T8 K
在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。) O* c; E6 U2 P; C
& e* D+ ~' U4 S E1 r" W
这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。
0 t: J! z' g7 g1 u+ N2 i4 B) `4 B7 X3 F" o
Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。: I9 q. k' N; J( [
( _' {2 ~7 q& f# V. U Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。
4 L8 L7 Y6 \* e7 V7 J% |9 O* M
( I0 W7 m: p- l( E8 N. qConfig Server0 x# }( t4 z, V: }+ U& s
) A E' T9 s$ l2 ?* F0 @5 d3 ?; s
Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。
7 m+ q; I' o4 W9 E. R
$ n% U. L6 _5 q4 u/ E 每一个config server都包括了MongoDB中所有chunk的信息。0 D; H: h1 P6 |$ h) X6 J
% J2 H, j0 ?/ d0 ?, m
Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。7 X" R2 y; S5 e; R2 {, Q
- [# X; ? L, g 如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。
1 f. h1 Z, D7 P: y
t1 o S% O t, r" [8 i MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。
8 B! s& I5 j5 s2 [0 x8 q9 ?- }
, j, J* A, U6 e3 p9 G( jMongos
, ^* Q% Y& m* c3 C1 Z3 X- x; J3 O/ s# c: Z# d1 w, p& o
用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。) L' D( f, e% R* }9 o3 F6 I; Y
/ A7 L/ h- Z! }, C& t5 }) G( E, b 当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。5 m& W* v; w3 V7 K9 k$ V$ p2 d! _
5 V4 ^, V+ V, J; H) B
Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。 \ Q( A% T& h
/ |; a/ ]; J4 q. A" v' R# \" ?
Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。* ]/ `: ^- }5 }; N4 i3 D# k
) d+ z$ K% U6 w, r8 Q* V- ]- m' h1 w
通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。
9 a" k& x: i8 T; i+ [9 m. a/ X: o
4 E8 Z. e9 I9 p3 e. m# SReference,
1 u" K7 u* }/ i% g: V) J! e( I- ~2 a
[0] Architectural Overview7 g# q' ]" J' J1 r* n7 U
http://www.mongodb.org/display/DOCS/Sharding+Introduction
5 L, S# H- r6 x4 Y, n, E6 ^; Q7 t y) { |
评分
-
查看全部评分
|