|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。
7 w( {% R4 m! V! d! U! M# m- u, e6 e* n. U+ H
阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?: I) E, j. P( v# h
* ?" Z" R; L8 m0 m, q; n' {) ~( b' j& K
不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。( k2 v7 b4 Q; Q6 v
^) Z* S' U; L1 w
: l8 n. {4 G s( w
; w$ C0 D. P; ?$ H! u8 ]! ~. t9 P图1-1 MongoDB架构图
; e$ r% K% A \6 n( N" ^# f" F9 @) o
MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。
' o. }/ j) H7 o: G% }- T3 V! Z; P$ C7 h# P+ _3 F4 {0 V
Shards) Z5 L* k7 A! v1 k
& p1 u* F* ~( Q/ Q) g- b MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。. o. ?0 s1 ~% X. N
& ?! c3 M- v: m" |
Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。' F; W' I { v2 h' w$ a- [
, Z o! l' a; p. n- U+ B
每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。
* I6 u2 E9 @5 d& k! }' b+ N. s- s" ?) ~+ S, b) p# V" j3 ?8 y
如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。& g( @8 O+ o( P$ v9 e5 [0 L: t
Z& L9 W3 c! [/ `1 r, T: H
Shard keys
1 V0 J, m8 H, e9 y+ m" T; G
" ^* t& L4 p9 H6 L5 Y, C3 A, e 为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。. i; e; c9 m/ @& l0 w
! I# W2 K% c8 G6 S
如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,# ~3 Y' A o6 M& x( {
5 g7 u/ v9 z, a. Q7 T' l( f{
+ v' l4 Q& G9 ]( j, V# Q4 S% e( p "ISBN": "987-30-3652-5130-82",
. {/ q P: I5 T, _, O% I* M+ d' u "Type": "CD",
5 X) N( N4 k" p! C1 e; g "Author": "Nirvana",4 F+ i- R Y. t" f+ f" U
"Title": "Nevermind",! N2 O% J, T: u6 X
"Genre": "Grunge",
1 V) K# u: }6 }2 K "Releasedate": "1991.09.24",
7 l. H. A. E( }# C5 [, | "Tracklist": [* d' W( D# ^5 m
{4 y7 ]7 X3 X: r2 D. L; l7 Z5 `" ?
"Track" : "1",8 }' t& q4 g9 ^6 j# D
"Title" : "Smells like teen spirit",/ U. C6 n" h4 R4 G0 I
"Length" : "5:02"8 w4 \! i2 [* v" M# \% o
},( C! B& L3 [3 i ^4 h4 s a, s* F
{
, \5 F8 r+ m, k3 ^ "Track" : "2",: i7 V1 R% H% n& x) K
"Title" : "In Bloom",
. K4 K$ ]' b2 h% ] "Length" : "4:15"9 J( i8 z+ y2 P5 k# b' t B7 w
}5 A; E9 r% ]5 \7 J% C# A, m
]
0 L. v( c* e( v* C- s7 F) I} Q' X; Y- W5 I" v
6 v0 W* U7 t6 P( [6 t& G- O Y{9 L; A6 v/ R4 H! S: G8 E
"ISBN": "987-1-4302-3051-9",$ G" z& l- l: Z6 G0 O
"Type": "Book",
. S' A2 @- U0 G: ^1 d "Title": "Definite Guide to MongoDB: The NoSQL Database",9 Z% c3 F3 a2 Z/ _* m
"Publisher": "Apress",2 `. t5 @* j, `7 j; {. l
"Author": " Eelco Plugge",: \% F) }! o2 W0 w
"Releasedate": "2011.06.09"
$ i" Q& R" h% g. J0 N! g. B, v0 B- i}# ~0 p7 B; u: T/ U/ o
/ Q- ?6 j4 ^. P" x
假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。
* n( ]: d1 c* u; Y* n( P! `) N) K X
! l6 s6 K- a3 J* o2 [5 R: n7 x 在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。: s0 \- D, Q( H6 H7 V/ O, j E" s
, t0 ?" o3 ^* O$ K5 E 例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。
8 ]/ ?7 p5 L, W' o
5 P4 L* Z/ R4 c8 W( K+ `8 }6 n 很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。5 @* J, c! Y, w/ A* }
0 @* w3 q* o5 S0 D, ?
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。
+ o8 S) R) F8 f" i1 o
$ S4 g: \6 {+ `0 rChunks/ g y; T) ]) z; }0 N
6 i. P/ {2 f1 J! l MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。
+ [0 r8 F+ F; M/ Q2 x$ p% @; S& E2 R" A* Q9 D
( x4 z2 `, ]- R: o5 z! n
图1-2 chunk的三元组 4 B% m( R r( Z2 t7 Q2 J. \9 ^
: V4 Q$ ] {0 X& w, \7 s 其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。
- M3 ~- n3 Q& r% y/ d# @4 m# R- T6 P' W/ ?0 n. A3 }
如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。9 f/ ~5 v, N4 |& C
4 O) {+ i! J v1 w! s1 p9 z9 M5 { Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。 O7 [8 Q, N; I* p
4 M! }) J- v# I* K! E( G4 r 一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。' k1 A9 A4 R7 |) i
& J$ S" ?( g7 m) D 当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。 R# U8 B1 ^7 p- S
. b1 w+ ]: ~( ?7 L8 ?1 X! _7 [8 @
这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。7 h, f2 g2 z0 L2 U6 n
5 \& A) V/ ]: _
Replica set
5 }$ p* i. w& F6 {6 `
' v/ `) c# L! m7 ? 在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
1 y ]# n. D8 ^' O
" {" D. Q: F7 D8 S/ K/ J 这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。$ C+ \4 c" B9 U, Y$ ^ M6 T
# w8 a- W0 r# ^* G0 Y) `. q& @ Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。* D Q1 E) D3 [3 ~/ ^
$ T/ o5 q* t' }, V3 U- n Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。: e% y; d9 @; a! f7 x0 ~2 w. ~
3 d, s( w+ k3 h
Config Server
+ x$ R/ s k( @0 v' O% w ; k! N( ~- r( A
Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。
E. {% ?8 C& S" A$ e5 }# p( T3 O3 Y3 T/ \+ d( ?
每一个config server都包括了MongoDB中所有chunk的信息。& |* U/ a$ k! C; b. n, A/ K
# l, o) n/ R' d6 c2 L8 L Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。6 h7 e# i- w, [$ v: W( O8 ^
- O i1 m, S* Y& z _ 如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。
) V" K/ o# D7 k: b' a. d- q8 n# P" z2 X" P
MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。# M3 r' Y! Z9 F$ o3 {' q
* o3 [5 ~; J, T* y1 |) p
Mongos
$ o' j: E7 S0 q, l; {! k# ]" p3 o& O1 l
用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。
& t8 J. s9 S1 r3 U( e8 g" J2 {, L: y4 C$ W: ^$ _, \9 R3 Z6 Z9 A
当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。
- ^3 u3 B" N' D
) J7 @8 y- b& W" V0 j* k3 b& o" ? Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。
3 G( Z6 Z4 g7 J G5 T" s, \% I9 f' c5 h3 V6 h
Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。
4 {* x n+ A7 N! f+ H+ C) x- `0 s6 h" I! e6 A
通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。
5 O+ _7 d7 r: E1 o/ E! t6 h( p9 K4 `, \: V
& }& m3 K/ D: W3 }
Reference,4 D+ X) D1 w4 I) g7 `# ?/ j
. l/ n; m/ n, e$ L
[0] Architectural Overview( f# y" L$ ?) ]) P2 S: w; S, q
http://www.mongodb.org/display/DOCS/Sharding+Introduction$ c/ Z* W' R* N P
|
评分
-
查看全部评分
|