|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。. @9 \4 v3 |" D
9 W1 j. R3 U5 b4 L% @
阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?
! ~( ^% K, W" L
, I( F( f$ L' s* Y 不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。
$ u4 U% V5 l$ k; ~9 o0 I) b9 M5 c
9 ]( n0 v" c t7 l. J
- r/ l% V6 z+ V5 N G; K$ S0 W- x
3 b* \' _9 C' f) y" z1 A图1-1 MongoDB架构图 ! Z- ]& q5 H) N' W: F9 ~/ ~6 _
( }& m% R& S8 _7 ?
MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。+ m7 X% w3 a( @4 @/ Q% u
7 l+ P! r5 B% k7 @" l
Shards. }( r9 f' c* m2 k0 v
9 q9 v/ |6 S8 a, A0 g
MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。9 V/ _2 q8 t5 b q4 u1 l% t3 \6 `
- p+ } P! _' d
Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。9 ^% P, x: y7 R: b8 b$ p8 B+ ]
) l* n% z5 F9 X& m/ N' W$ S* r 每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。% p- c: E! }2 G3 l d
/ f: l* ]0 [1 A6 W
如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
, v% Z' a$ |: {
8 L; W s2 c4 zShard keys
8 u) T0 O" d. R# t
4 G0 R4 g, k I/ | 为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。
) y5 @6 p( C0 i1 Z4 Z- t
, V* P. R" E& h: ~4 T% X 如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,
5 P1 I: K: }+ J) m
w( p& x x+ v( D{9 l- q- ]1 ^( u4 P. g& g
"ISBN": "987-30-3652-5130-82",0 n. \6 M2 e5 w/ l+ _
"Type": "CD",
( n6 ]" x4 U- t) S: d/ C; A "Author": "Nirvana",
% d2 Y: E! j$ ^( a2 N" T% J "Title": "Nevermind",
1 p# |) a" I! Y/ V7 F' Z "Genre": "Grunge",; K; }( P5 U, m& q
"Releasedate": "1991.09.24",4 O& S5 n5 S8 _
"Tracklist": [
. p% g7 T8 G/ U {
/ r4 W/ ]9 s; p" m "Track" : "1",+ j; _- A0 ?, s3 P6 [% f5 q) u, [! r
"Title" : "Smells like teen spirit",
7 ^6 ]5 ?3 B, Q- H9 o# H7 S6 b "Length" : "5:02"$ P# w6 l' J8 f4 ]# F( S& `+ O
},& \& W# w8 R8 Q4 S1 D( f" m
{ d; f# @& U( u+ {; A1 C
"Track" : "2",1 T! E& y8 }! Y O- l
"Title" : "In Bloom",2 D( A; J7 ?2 |- s
"Length" : "4:15"
' {7 E' } l Q {9 |- E }7 W1 ~8 l( w/ h, k3 }6 J
]
9 K l& e0 f+ `' t: Q( A" }}
) `8 G; X$ S+ u! R; W" Q9 O6 N( ~: g* U; b
{" T& [/ \8 m9 T* c' o
"ISBN": "987-1-4302-3051-9",
! M7 C' R2 s- l) u. X "Type": "Book",
- M. T" a5 V& D- d "Title": "Definite Guide to MongoDB: The NoSQL Database",
9 S* x* t3 K- ?4 L' u: C4 q- K "Publisher": "Apress",+ h9 i! R, l) o8 ]; x5 P( I+ W
"Author": " Eelco Plugge",% E2 g, L/ M) {/ m4 @. S6 Q
"Releasedate": "2011.06.09"
- s3 P, ^, k( c" Z/ M3 B! d- V}: N6 f; x( R% G$ ~& A. g9 O
- E6 D* g& y$ |& A4 W* `7 w
假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。+ K( y+ z ]; i" i0 ^0 D
) E2 O0 c3 M0 i/ `3 |
在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。
: ]! v' L( K1 S9 @ l* }$ [# B% l5 q' O3 F, b- x. H
例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。/ u4 O: q- d- U9 w
9 _) Y: [+ D( `
很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。( f+ h1 F: n( G5 [) v0 G
+ Q |5 y) I0 F2 @2 A1 `
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。% s) y! X3 D% W$ o1 m
$ q7 d# C4 d$ n1 z$ W) q
Chunks7 [. ]' B7 ~- q
; w# K' Z2 Q3 |! }
MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。
+ n0 E6 i) k: l# A; g
1 X) A1 x4 ?7 i' A% S
. o* ?" p U1 ?5 }图1-2 chunk的三元组 ! U5 \$ m' o6 U
7 V6 u/ I; o u1 ? 其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。
4 I0 x, Y, \% G
" v5 Q9 _( K. ^& E- Y- c( L 如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。, x: ]# u# e+ ~4 S
5 y2 V+ d: g9 _+ x R: ` Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。
4 S* \. {( G' e" {) ^9 u6 c J! }* X' d
一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。
$ f/ y: r8 I7 j7 g1 r3 V8 Y- n+ W) p5 b# r. U
当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。
+ e3 }2 a( g( g, w% o
+ j0 w4 O0 B5 ^9 m4 c 这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。
j5 R3 g3 f ~/ E3 @
# ^" h) l. b' H" M1 n6 o) P4 xReplica set4 k- _4 n9 n1 D' k) @
7 \6 x9 a- x$ _% b( ]4 q2 }- C
在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。0 ?. s0 g" s3 M! F+ w% p
4 e% F$ r7 S% A9 W/ I* m2 Y 这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。
' `! g7 G3 z$ |; T- t2 k- Z; }' M% v$ ?9 \- w
Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。
7 b4 l8 W% `% D ]- @2 [
3 R! Z L2 f w* I) t* Z/ H: q; b Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。
$ c0 u b- ?6 w( f% o0 `
0 R1 ^# R+ m% UConfig Server- d. C$ l. R; n2 O
% K0 y0 {8 @; o& U E) t1 d
Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。
7 n9 y8 Z0 A" q# v- \. E: s* k
1 i5 Z2 e3 U8 D3 \& `; ] 每一个config server都包括了MongoDB中所有chunk的信息。
. H3 P/ w5 G9 P" f" v$ p
) h* ] E% S* v4 g3 J- o6 r Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。
5 I& l& ^3 E2 R }0 I2 }& `( }& Q- |
如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。
) t2 j- m/ I5 G4 e$ d( c1 _) Q' w; Z9 Y: o& s1 C k. {
MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。" r1 a% C2 L, U
3 [% U) M6 P1 R5 K0 _6 @1 }- u# H
Mongos* [9 m, z9 f) v+ U+ q3 k
, ^7 f& Y- y# R" f6 p8 T/ T 用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。
* T$ ^2 [5 p6 T, `7 n& v9 Z& Q* v
当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。
6 n2 B' N+ I: _# e
# G) w; ~% e* N# Z, e4 ` Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。
7 w$ |$ N! s( d! z# L% {' x. \5 w8 d7 U4 R$ N, P
Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。
$ U7 w0 B! N: L$ X$ A) g0 S1 d7 {# |$ w
通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。
* ~( v' s9 O; P& ]: `
, x1 m& } H! R8 [4 B2 X# k! u4 o+ M$ S5 F2 F( C& P
Reference,. |, `- D& H2 L+ b7 J* J
+ g }" m0 w% Y
[0] Architectural Overview* G) P1 o( U' u2 y: r8 `( R
http://www.mongodb.org/display/DOCS/Sharding+Introduction
7 u' \# m; Z1 | |
评分
-
查看全部评分
|