|
|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。
6 K, s( W }& }( W/ L+ [- z' P! |; e' C6 e. k
阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?
0 b" e0 U) Q, N' h: F( O
- q3 I" L) L5 l6 X: t: ?5 C) O 不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。
" Q" W# H; u$ |" h# }: v3 ?
( p+ [7 _- }. X8 t& N5 v
* O2 M4 u+ x+ S A; N
; T) {$ R' N( ^6 Y- s图1-1 MongoDB架构图 @9 Z! x+ a, o8 e B
2 y; q, J/ B. _; C
MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。7 ]' d. J" |) B8 |
7 U e- r. ?/ _( Y' X. x) ~; UShards* v8 b% G8 ^1 ]% z7 N! a, L3 J% T
. Q" r" _* ~$ u' f MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。
8 p; M1 n, u' G% Y/ B$ k: F" N* x, |) O6 `* k/ A
Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。" b: S% K O3 G- x
, a5 |+ E; A, H' O 每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。+ _1 J' ?$ U* h
: }0 F5 ~" l, i8 P; A 如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。2 m* @9 T ?) K" p
1 E# e+ P& P5 J+ E
Shard keys8 |% S2 L3 p& K# \
) F* L7 A3 I# q* q3 m 为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。
9 |' N# t# J7 ~: f+ s3 B1 J
7 ^9 D; ?8 X$ M/ Q 如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,
h2 _* W$ D+ y3 l- ]% i0 D" i: S
; J! M6 I0 _8 h6 |8 C/ L{
! g- S6 }. f' ~ W1 @8 \, m "ISBN": "987-30-3652-5130-82",* O$ ` ?/ S$ L! Y
"Type": "CD",
2 `, u7 w }* _+ V "Author": "Nirvana",& [ p) T0 _3 x, `
"Title": "Nevermind",
2 `+ o9 c- |& T& W, F/ n* e "Genre": "Grunge",1 P1 B( i( a/ ~+ O7 w
"Releasedate": "1991.09.24",' a1 r' i, h% L8 k' ?) }
"Tracklist": [
# R* K1 t, Q$ F K {- j- n' {1 ?" O& a9 {+ i
"Track" : "1",
0 U; H6 I: U9 J/ I; u" m "Title" : "Smells like teen spirit",
& e4 Z" M0 X6 p; { "Length" : "5:02". j1 Y' o1 t& i# a/ u
},
/ P& p2 O1 Q% _3 Z1 J3 T0 U6 ~ {- A7 w/ R; v% o* ~; c9 K
"Track" : "2",
; {- \) {( t' r1 K "Title" : "In Bloom",. U9 P9 ]2 T8 U
"Length" : "4:15", D% r% j9 W; u1 P0 O, N
}
* d( R) b, Q- @' F ]
$ r- f" O$ \/ D& h}8 J/ C: M6 t' i5 Q4 v* l
9 a5 s% c( S3 t# \
{
8 l% D8 T5 C2 I "ISBN": "987-1-4302-3051-9",
$ _$ Y- n6 @3 Q( P "Type": "Book",; w! c& U1 T5 w( R. f, a8 q: h! Y
"Title": "Definite Guide to MongoDB: The NoSQL Database",
6 h d# G# m5 I "Publisher": "Apress",5 K8 X* d# `) j( ]5 r+ r- m+ R3 N
"Author": " Eelco Plugge",2 c+ U. K% H: x( g1 e& e X
"Releasedate": "2011.06.09"
" M( \8 x* B4 w F; K}9 c! ~3 Z u! V. ?: o$ o
' D5 ~8 q& z) H$ S! H# z; ?$ `6 H
假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。
8 H- T; h& {" w% B
% y2 B0 T7 d4 w' ~ p# d 在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。
5 ?0 H- l* A$ V! R
+ W+ h0 O- ^2 Z' g 例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。
. z; m* E/ [( ?/ r. M* R( ~" l v& C+ y; O* M$ a* s5 e7 R
很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。
5 F3 y5 P F/ w4 x3 t( d2 D* d7 G/ t5 ^+ t0 H. G/ g
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。
& s5 Z2 ]2 S' D* ~& [5 }6 f& ~9 u+ K
1 D( H5 j7 O' V2 f& C9 N0 ]: t) A1 GChunks o) h1 o+ m0 ~1 Y1 Z; s: Y3 O% z
( x q& `) O5 N" w& @ MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。3 J& }* g6 N, \3 a% a* y" b- p" r) g) V
9 i& o& @9 z$ e6 d; I4 `1 Y
u& ^' B6 ?+ d' P
图1-2 chunk的三元组
) g- @0 b7 W, D+ v* C" L6 C: T. N; r. S1 X- d! b7 g
其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。: p$ a. l. x/ h
( j0 b/ B5 b: ?+ u9 l; [' l 如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。
/ D7 M g' K( ?, q1 f
1 m, w' a# ?) g# w) y/ D Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。5 y% |+ A0 |( K) m: v# {6 d
7 m+ w" h. {& W+ A8 O. i6 i9 Y
一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。7 l9 t2 L0 Z9 K5 B" x
4 {; F" ^7 b7 ~) B% i7 h# ~
当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。! N5 x' ]/ h& S) O- _% F/ P
6 ~; L9 n$ c4 Z' n
这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。, D9 z5 B& j; ^! Q4 }, E8 n# {
/ e. S4 S) @( C* k0 w% v! {Replica set5 M1 F8 {- `9 }& v7 g. \( L+ Z; c% ]2 H
) D4 e5 m( f5 |5 e 在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
4 |9 ~6 N0 z( r: W" r! \& `
4 p% N3 t' E0 x" l ^0 P O 这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。
: j: F$ d% a7 R; F( U+ e3 K; g) q- m! U9 B# o0 ~+ {
Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。! e4 z7 Y. N5 _2 @6 `% h O0 K
9 w+ f9 E( t ?5 x# |2 W
Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。
. U; w7 f; f2 ?/ I4 C8 d
% e: [4 l% _8 `1 ^# j1 dConfig Server
& }2 [2 n0 Y0 z" A o# n/ I7 s- `
* u# w" z( q& d1 {- r& U+ e Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。' }$ T7 X2 U# c6 p5 }
) W. s3 J" p4 \- L9 A
每一个config server都包括了MongoDB中所有chunk的信息。
* c/ |( g. K0 _
7 W+ V9 e ~' \: x Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。
, b* F3 @$ R: L5 R* W
' K# Q- a/ q5 y: ~8 y6 B 如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。& C( G# u; c8 g. i
- x, R/ I" z, c1 l5 h( [
MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。( W T4 u l. q
/ }8 N) {- x( A# F# I0 j/ `) {' X
Mongos7 n# o+ E8 x3 [* G+ ^3 _
/ Z8 b& {5 J$ ?/ y8 ?. Q( Z y6 t 用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。
" M' e8 d+ K" y" G* C% I O( M+ P m5 T' e ~, a2 g* B& G
当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。: _% E! d+ T, B+ k
; I* r& O: k: N2 j. f
Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。
. k1 _0 T1 I, Z: u0 j+ S6 ^
4 C% f3 U4 r, N3 \# G4 c% ` Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。
( h u+ H7 Q, G6 I, G8 [3 e* g* [2 y* _6 `
通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。
6 {2 O4 I S2 Y5 _) v) F( i( U0 C5 C4 r5 o" Q0 G3 y T- f: N
% y6 w1 z, J4 |" e! Y! [2 F+ ~Reference,3 L9 v3 \2 g; W, d1 g8 ^
5 X' Z1 b% y2 e
[0] Architectural Overview* f9 N& U% o/ q) T% T
http://www.mongodb.org/display/DOCS/Sharding+Introduction
1 }2 V, K7 g7 F& D5 b |
评分
-
查看全部评分
|