|
|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。) ^- c& @$ X; {( J1 p5 _' D
V& A# [4 A+ ?+ |% q+ ~" K 阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?
5 h, z- j. M" F: {: w- P {: Z' l" P/ F. |" @+ T
不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。" j/ H7 j8 W# m9 V* u L& [1 ?" c1 B0 e
# c* j5 d+ I( Y$ l! }
. J8 [$ k' ^- W( g
/ a* E" J8 ^* v% L: |
图1-1 MongoDB架构图 ! V/ {& x8 L! Z$ T: x( n
2 P! [* n" m+ w9 G+ }; b Z
MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。
! j7 [- O! Y0 W3 I
/ Q+ I6 V- @7 C9 E8 t7 P3 qShards# E. g' `/ x% w- w8 w
- L X7 y& Q3 E- Y; V8 P
MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。& d% i9 B9 W- O$ x. j) P; }$ D+ v
0 U$ ?+ b- Q1 [# o Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。
5 G) N1 n2 L2 `& l& P0 L* D# j7 ?9 U
每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。2 U! ~. k) r: X
U0 s6 B$ k9 R; V+ Q+ ] 如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。7 w+ V( |! w# I) l/ d4 k+ g
3 {0 G6 f+ E8 G+ a+ f
Shard keys
- y- U+ X9 K8 i6 @ 4 o& O; G2 |9 _" z* B
为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。
* Y' K$ x* h) S W8 ^' d6 h
1 e1 F0 _0 e: n1 E$ f 如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,$ j, F1 n2 F. t% [& V) Z
# w( [& U+ P+ y0 k) F, n( S{3 m1 C* R) ^- C4 ^* k
"ISBN": "987-30-3652-5130-82",
' p# A: G, N% f$ ?6 e" H7 p* w! R. Q: i "Type": "CD",/ z# {2 } Q! ^
"Author": "Nirvana",
. Q! f% R$ c% Z2 S0 S "Title": "Nevermind",
4 d( G8 r6 P* Y2 T) E" \3 Z "Genre": "Grunge",
7 e% ^7 ]( {1 f, m' i8 y "Releasedate": "1991.09.24",0 T% s1 e' B$ Y( ]& s
"Tracklist": [
3 F( S+ w, H$ [/ c8 c! E {
J( ]6 R" V% D- {) x) A "Track" : "1",
g) o! z& L$ r4 q2 W; \ "Title" : "Smells like teen spirit",( y; V6 x3 a; x+ }% v q, w' {2 d
"Length" : "5:02"
/ u$ `* P" ^. H o7 k- Q* T8 V },
! e; R: G2 Z- `! P( v0 m2 ^5 g {* W. V1 ]" U1 _8 j- ? s
"Track" : "2",: x3 w+ C6 x4 v( c+ \+ h' t
"Title" : "In Bloom",: w/ Q' f3 v# j2 G
"Length" : "4:15"
' I9 n0 Q4 s$ ^) r$ y }4 ^) ?, n0 u3 ]& j2 C( K+ n
]5 a0 C9 V0 `, k& u
}
, u* I; ^' c) N7 T, ^) H* _8 h# W: C% n @$ P7 p# Z% f
{ e4 w, c: f4 r/ i& {" g! B4 l0 e; C
"ISBN": "987-1-4302-3051-9", q6 i5 ?! L, y y: f: c d
"Type": "Book",7 L4 W& v5 U4 F) V
"Title": "Definite Guide to MongoDB: The NoSQL Database",
# k: u; U8 g y "Publisher": "Apress",: m2 e5 j% C; _$ y2 P
"Author": " Eelco Plugge",/ u7 b8 |# P! @6 k7 |/ A% e9 n5 p6 ]
"Releasedate": "2011.06.09"
: }% S' T7 D" B6 U}
$ W& W( V7 r8 C4 r; r8 d7 k$ ^
5 k: g! z- a( `- a, S2 W6 K 假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。
) j3 p: {/ F1 Q% Y, x H# \; s+ a( ?3 u: F- _
在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。
( T1 K6 N1 }" f E5 R$ U7 h) {! x; E0 h4 W& @
例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。+ d0 N3 w; X& S' b3 D) c
5 I7 ~3 v& H0 T% K8 ~ 很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。
) O6 G: d0 X" W5 H. R$ I$ }
3 T o3 s$ L$ A! j5 G4 z 延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。
" J4 C4 W( u/ U/ m0 F, }; h2 y- A/ J( f! M5 K# Z$ E( k7 e
Chunks' ~+ y& m" h, W/ T6 J
0 o/ G. [, C( E. D; c/ f% U
MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。
7 c) Z0 R2 v3 |; X1 Z6 h6 @8 W2 u( I D2 i1 j7 G3 A+ T
& p4 t1 n( ~: f8 y0 q- w
图1-2 chunk的三元组 5 P4 ? b) s' I& ^/ c$ e
. J# u- O3 p# S: ^' S( H 其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。
2 w6 B) ~$ N/ B2 P; b, m# q9 W( \6 y9 ~7 {& [' D: d" s
如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。
5 v. w3 [/ V) e. o% M0 }8 P% L& ?5 `0 y5 r
0 F3 V! o9 v# x! g$ Y, I' l Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。
) \9 ]4 h2 J; ?; Y+ ]+ P, Y4 b! ~3 ^: a/ @: R
一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。
! {9 Y( x) @8 H4 [9 r% Z0 I( n8 ~) G1 s& ^5 k v7 s0 V6 [2 H' U! }
当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。% G* L e B) Z2 C6 `
0 R/ M/ d9 O& z3 \$ e" B 这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。 Q/ A" a" x% t" q9 g3 l! z
* |) M' n) z8 s& K1 FReplica set
: b' ?' [% g8 T, I; H 2 C9 A( Z5 U4 x
在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。 n! \/ O3 d9 y6 {/ L5 l
8 M/ o& ^6 M7 a
这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。
/ S, ^: h0 M" n: R1 x. `0 ~; D" P
0 E8 P! F+ E- T/ E% ^ Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。
' i& g% p" n$ u1 f0 h& K- q; O1 N6 \8 [3 F
Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。* a: B" S9 G2 Y
2 C+ E. p! H' g7 R0 ^' O) l2 zConfig Server1 Z' Z G. R* v5 ^" f
+ R1 i. l6 y4 O! ?) o Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。+ f1 \5 } B$ {5 V+ T
; q% ^' c# g4 I3 L8 W/ h 每一个config server都包括了MongoDB中所有chunk的信息。
7 D0 l: o4 U( a- O, m/ h" I# h, Y: N
' I8 l y, ?7 T6 E* n) \; b Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。
# C( g. X/ t$ A: u
3 W$ e* `- B2 D 如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。6 m1 Z$ w# f& @+ ?( e
& D/ z' p6 F! q& H
MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。
/ ?3 ^$ R9 b# K9 k, {+ z1 ]
& K2 U/ J2 e- n1 ]8 j) x# l. EMongos( r3 ]8 I. S6 @; p- F1 v+ T6 F9 N
4 H0 J( ^' h! I 用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。
8 j- S" i4 U: U7 U; b: A3 S9 _( O
$ p' {( _3 z- C/ H/ G" u 当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。7 _8 [8 O2 T* B, y
" k; o% Z" _! X9 B% |" O
Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。/ E2 A; A' g2 d8 D5 @( F
) U4 M. F0 [; S! b; C8 @# A Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。; \+ b( o% S2 b9 }$ O! k
& d) E8 P) a, E& | 通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。9 w6 \1 H; G" J$ t
" |) q( V- Z9 z, g& z" G* U
$ h7 S* v+ R* {$ b+ p. \/ S) \Reference,9 t9 p( u# g9 q- n6 z
- G/ Y2 K4 R4 G) P5 f' c
[0] Architectural Overview; k9 S% r8 U9 P- l! @1 r! [
http://www.mongodb.org/display/DOCS/Sharding+Introduction$ b S, \ }5 U# E5 _# Z2 i
|
评分
-
查看全部评分
|