|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。
% C( F9 V# ^8 h; h9 j+ ?7 ~8 ~$ h( i9 ^, ~% p
阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?
: P9 l# F& s: Y. L9 j; F* v0 i }: `7 T
不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。1 b& q( B* C5 U* i
) R, }& M1 x5 K; h6 k! J: z
+ |, V/ {3 |4 N1 x0 _" ?/ f, I/ f! U9 v2 C5 Z( N. P; n
图1-1 MongoDB架构图
0 i! ]* I5 z K, R
7 o* ?& h% O8 u$ E/ _# {% D MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。. D* N" B! L9 {' \
0 E8 _# g2 ?: `Shards
3 O* z- g0 U9 o2 W+ p, s! A/ y& d! e% S' f: L- i
MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。* o5 G v/ m5 c: h7 L" y
* Y- q7 _, F ` Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。
5 l- ], r k( }$ i3 V
& F( }: d8 E1 n6 z F; t" { 每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。
0 B3 F7 k4 `7 u, n" L! m9 h3 P ^3 t9 E4 ]; F* P5 B
如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。) c7 A* G. O6 Y8 p# O" b' l
, ~) I9 @; l# h+ f- b
Shard keys
+ @! |' Y/ a% r
8 s' D% {( n; G# A# n4 v 为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。: V2 o( E A* O$ T$ N7 q8 ?( r
7 _9 }9 R; ]# v& {3 \& @6 Q 如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,
6 _# O4 `( ^4 Z" G$ N3 u2 }+ u, N7 `) F) p1 A3 f) e l5 i
{
# E# P7 \( V& q! B, P "ISBN": "987-30-3652-5130-82",& V7 a" d: {2 J$ Q5 g* W
"Type": "CD",. e, o0 E; R1 e- E [# z6 e
"Author": "Nirvana"," ^% Y2 e" H1 Y2 q4 {
"Title": "Nevermind",
/ u* q# A0 H+ H! [ "Genre": "Grunge",
6 U9 c( ~- C M "Releasedate": "1991.09.24",
/ _1 B" W5 y% I1 d. L( C( ?/ ` "Tracklist": [
4 e2 e- F4 B' I$ Y9 | {& `& T% ^( c4 a: z1 H: M
"Track" : "1",+ E( L$ i) m0 Q# |, D
"Title" : "Smells like teen spirit",- U3 Z, _! ^3 f. w" j0 q1 D& p; f
"Length" : "5:02"4 j+ y7 H2 q; Z) V+ i$ F+ g0 e
},
6 f& S+ @7 W/ l- \- ]$ q {- b$ H3 g; U2 l& {4 F. y6 B
"Track" : "2",6 v9 S0 ]# E- l# v! Y$ p
"Title" : "In Bloom",
6 ^ O( [6 _9 c "Length" : "4:15"" z& r, n4 \& G; ^ x
}
3 A9 V% Z! m" P' ` ]* r$ N; U1 X; }: r' N
}; z7 {8 N# Z! z
% h. p- c6 o0 p+ Q6 ~- ]3 u
{( ~' X- R% |- T/ T5 V& k; A
"ISBN": "987-1-4302-3051-9",
# L2 v; e7 v) w% q# z; h1 @ "Type": "Book",
: x2 ?1 [# [0 u/ l/ F! i "Title": "Definite Guide to MongoDB: The NoSQL Database",
! c7 H9 d1 T, [& n "Publisher": "Apress",
2 g# D7 i" o0 S. C5 @8 F "Author": " Eelco Plugge",
2 O6 `3 O) x2 G& S, k+ E "Releasedate": "2011.06.09"6 o( e; T, U" ?- ~5 j7 n) {
}5 }2 \8 v! J& y L% [
5 M- M. j' Y9 x9 W. R
假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。
: P0 G8 b0 Z; P3 P( t3 A& w4 B! H
" k) T: E9 S5 M `) O 在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。9 t" k, t$ f$ u. z9 |5 ~
a! X6 A1 x5 O: ? 例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。 K& A# o7 }* \8 H8 m0 B; @ U( W1 y
& n: t% L& P7 C4 p. |. ? 很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。
* ]' q9 ?/ s# m$ k5 H9 f1 [% g+ A* ~( C4 {) \5 }6 q
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。4 P% Z8 b& _. T2 e2 F- F* W
/ G! F: k% x* b0 \& N- T- g
Chunks r! R8 w( i: h; Y1 e) d5 ]( n+ l# a
# W! @# `+ L8 @* [$ r( l3 s, ]1 b MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。) E8 Q7 d3 o; H/ s+ t
2 b9 C/ N# x% Q
- E, Q; G9 K7 I( Z) e% D7 {6 Q图1-2 chunk的三元组 5 \" F$ c- Z/ B8 H6 ]# j3 c- V9 v
! M7 n' s5 l# Q. g
其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。
5 T- w* H& d ]" W3 c6 z( q
1 M+ j @+ e! @# V4 A8 b 如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。
$ ^/ a) L d5 U, Z5 U$ j3 _: M P. a6 I' j
Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。2 @$ r8 M' d! t7 ]1 }+ o
+ b1 V3 K2 e! m3 `& e7 F2 e
一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。. J2 y$ N8 b* l$ j3 `. T
2 x" D4 { w3 e1 H5 a' o Y ^ 当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。
% j. M" f5 V5 i+ W) y1 Y1 a( j
3 d/ y7 K& c# P8 D! J' _5 H 这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。
k( y. A0 D' t5 w0 e' f% z' ?& v$ n8 r% ?6 U# s# F5 ?4 u {! ?
Replica set/ @* h% O5 h$ J% q; u: x' N
8 F9 _1 x" g) g: k5 r
在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
. @& K+ }, N* d. h# y3 f9 \6 T+ N* N
这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。3 {2 J. S4 f5 d6 ~0 V+ w' z* V
0 Q6 _9 f; S& m0 A( C Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。
% h9 B, y3 G3 Y, S9 ?1 ~1 q& C9 b; l
Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。0 S7 s5 ^3 Y7 ^" x1 ~& C
! @, v, J3 {; _ O, sConfig Server
( r# e7 x8 G. H& q
+ Y& I8 d& b7 a Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。
# T' L! t# x) d5 G
( t" ]2 a: I- b+ ^; ~ 每一个config server都包括了MongoDB中所有chunk的信息。
b3 _+ X4 S1 Z7 n0 V
* o2 y! `; F, c5 N3 o Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。
0 r f; |" J7 S0 t. `3 o3 O0 M1 S$ N% _" N& Y0 [! s
如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。) c3 v5 ~' d" H" A
/ s: m5 m' z( S5 K" x* K) ~ _ MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。
3 v3 }8 \! G1 Y1 n5 X, @
7 h; K& ]5 Q% {" F: @ E/ s! O7 OMongos1 A7 y2 d' `) s8 |. O! ^6 W0 w+ O
% U& t1 X& O4 c9 o* p# ]
用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。: ?' N8 _4 H( S6 O- s* r, a
% g* G9 Z9 w2 Q# p( w6 o5 v
当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。
# `: I5 u. }& D4 Z+ c! k
3 ~6 V2 [% R1 u( ^" }' [ Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。
$ T0 o5 l. U3 e$ o( R9 e' G" h7 L) D
Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。
7 Z/ ^# n' i6 p/ y7 \6 V2 L( i$ `" s
通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。# J; }( a2 f$ B# _5 R+ |
5 K' \7 x P8 P1 f/ t8 ]# L! e1 m0 e( w
Reference,
+ Z, W% p( ~4 l* P3 D6 _, D6 q0 Z- e! J) K
[0] Architectural Overview
+ u/ v3 D5 S* G3 E5 ^) l3 Ohttp://www.mongodb.org/display/DOCS/Sharding+Introduction
) [( }8 ^+ \ k |
评分
-
查看全部评分
|