|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。2 z9 P b! n& f
( r' ~1 W; W/ q, x 阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?
2 V, C0 T+ I8 `5 V/ i5 o5 T1 f8 `5 h- M1 d; ]; r# w
不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。
d* i! D0 T; J, `- f
7 d4 \. ^7 M' l7 I6 F
' H/ ^ C1 P; Q. ~0 ]5 a
' |8 N" [+ ?. y1 I图1-1 MongoDB架构图
H" z2 `2 b5 w, x$ r
" t( I* I$ E5 } MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。
" T" W- y8 t( R7 V2 U% g0 V8 b% H7 ~ c" f. l
Shards/ h3 O8 }+ R" @! ?
" [, a% n; T; ^9 c, r. Y0 Q
MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。& \$ J1 f2 u- H, Z/ V7 W Z
9 ^/ g! i" v- s, V. b# e Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。
6 H) O" H; Q& J+ E
& a6 Q9 I) r; _ 每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。
7 @, [9 U' T$ N: D4 A. I* p- e. r/ ?3 ]' L# V
如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。! w# L2 B& J& e. N
5 G$ @0 M5 I( R+ ?Shard keys9 w! G8 ^2 [! F4 C
% F: `$ F# |/ y* l" d/ i: Y
为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。9 A" ~2 M2 p* ]! i: L6 w' o. F
+ J. R7 c6 g8 t# B I5 r 如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,# M C- ^: C! G
2 E$ P1 S0 r9 z+ l, k
{
. [/ C# J7 z" ^) @" k- \; A2 F/ f "ISBN": "987-30-3652-5130-82",
% m9 z5 m7 l6 S1 f+ e "Type": "CD",
3 J$ W$ i5 N% k3 e- J1 X( w "Author": "Nirvana",
; e% M3 t. |; ?2 s) h* R3 B! N8 @ "Title": "Nevermind",6 g8 b1 T7 I: K+ M4 w
"Genre": "Grunge",8 h7 V+ U- e# O+ S5 i. d7 ]
"Releasedate": "1991.09.24"," { U! F0 v# f8 \3 u2 S
"Tracklist": [* s3 C N" t2 ]
{* V% L- @7 D6 ]7 H* h3 R
"Track" : "1", Z8 t3 S! P- G) N, T$ `
"Title" : "Smells like teen spirit",# M5 E& M2 @+ A
"Length" : "5:02"
3 b% |- Z# c0 R6 }) V },0 H. f+ F* [2 w. B! J7 n
{
3 Y- b+ A# e* H/ X2 `# O! l/ W "Track" : "2",
* T. n ?. }8 |2 N+ r "Title" : "In Bloom",
. H" f7 B ? f8 F5 \ "Length" : "4:15") C1 v: _' [' u+ t% w/ L# j& u
}
) ]- B+ O) W1 `. {' U ]. b1 d3 C0 Q) @' v. {$ z
}
; t( B* a* @/ k8 G! r0 J
; Z0 W' V6 q- @ e A* x, {# G{
& k/ }; Y5 G6 M' M+ T$ k7 ~ "ISBN": "987-1-4302-3051-9",1 g1 _, Y, n: |; X j8 f4 y6 u
"Type": "Book",/ O5 v5 h( c5 D' N1 S
"Title": "Definite Guide to MongoDB: The NoSQL Database",$ V, [! q0 s7 {6 n/ F9 [
"Publisher": "Apress",
6 _' A8 e, O1 l( S6 r* g "Author": " Eelco Plugge",# Z% }% G+ P, j5 q n: Q
"Releasedate": "2011.06.09" y% B2 H/ ^, x. x+ c2 f, g
}
7 t9 [, ?% H* S5 d. _) Q. l6 l! d, o3 b+ o: s
假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。2 b5 C7 c2 J: v
" ~5 C2 T' b1 L+ c! V! U6 ^
在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。1 y7 T7 F# _& L. f5 A. n7 X& {
9 Y0 j! a4 o, X6 }$ R: x
例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。; P3 Q" v. L. b' n6 o# m1 m7 n0 ]
9 \, b4 u/ m% s
很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。# g; Q3 T! P& w: M' n
% l' _3 s, _: T: t
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。
8 r7 _) e0 p, h! C% i
2 g2 p, ^$ @1 S5 I' J, n5 FChunks1 P& D& p: F# N- R
3 t& Z4 S+ k) E1 B$ T* N4 ^& T MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。% R; }1 q# h$ N b
+ y5 M) a& { [. y; ?9 z
' Z! w: M3 J9 J6 @* ]( j s
图1-2 chunk的三元组 o- b y' f. x5 Z& e' }* n7 Y
6 w: [$ d& J( J
其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。7 X) Q; a9 |0 j
% r8 E9 P- {2 \ C2 y. Q5 `) w/ l% V 如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。& B& D- W6 J @
9 J% E. _1 @0 k* Y0 @% } Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。
) s& Y3 D6 ^9 `2 M
4 {9 ?5 F' B8 d) P2 V$ ] 一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。3 J9 n3 d% V& L# u
1 K) H7 ~6 b0 j- N4 V
当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。
- q# V+ r4 `, [5 m0 i
9 \3 _# S% _2 R4 Q 这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。* |" B* X+ N5 i- m% y8 t: t
/ q; ], L2 p/ t
Replica set. J/ u- }2 ^& t+ g' D
8 h/ g9 J" U7 S7 W& q8 r$ j 在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。: A/ e+ F0 H; S5 k' A1 ^
# A0 R3 ^7 u$ A6 W1 ~! X% f- B* \1 b
这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。; O) K' c0 Q+ r0 V( C9 B/ V
; _+ u6 Z/ A. k/ A9 ^
Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。
( T4 m* s- s" d
, p% P/ d! s! y Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。0 c+ W! W# D; U8 j4 Q7 s. n
y) y. o2 Y+ y* `7 [& H( g
Config Server- b/ a' j2 K% e% j3 B
' v5 h8 {, q8 o2 N1 u
Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。
$ {. c i# i' ?# T5 A
3 Y' \9 y! J$ ^ P' _: c3 T0 M 每一个config server都包括了MongoDB中所有chunk的信息。
( K o, H0 F! Q* \4 a4 E; M
0 r* T! x B/ G- x* w Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。
2 M& U# i, \, Z$ {, {- y* M* ` k" K: W) p* y9 H: a
如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。. _5 `% E3 O; g1 q7 H
* t9 Y# a: ?* a
MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。5 J; F% n; x& T( \; d- t' o
; T* O2 h% O0 U6 y" ^* F# X! O$ M
Mongos' \5 ]1 `9 N, e# D0 g% W+ ]; e
2 t# q/ x* p$ T
用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。
& q& s4 f8 k J( }" {9 q6 q8 _ i: h; |! m
当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。" Q* I4 V) D1 a; p7 g. Q
+ z& ?* h1 Z( a! N0 J- C$ `+ D
Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。0 X# M; \" a6 H: g3 D# g$ i( I
; E) x' S5 t6 V2 X( S Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。5 q! o5 b1 [; D+ H3 `
* q; e% _ W# i7 _- B( @+ Y5 m' o. x 通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。$ m J, o& K7 ?1 _7 }7 A3 i
' i Q4 K2 r" y1 n1 y- V2 ~
: F; ~! u1 m- _9 l& C
Reference,# w4 J: Q, |, G6 m* F9 h: d$ ~: t
2 }4 N+ t. f8 @. D5 H& W
[0] Architectural Overview( v$ f v* a7 H: a6 b; l
http://www.mongodb.org/display/DOCS/Sharding+Introduction9 y7 B5 K j& G6 A; L: D# W
|
评分
-
查看全部评分
|