|
|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。
4 h. s! l3 h' Q1 C4 U/ S' o4 p1 t6 V. m
阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?
: _" r1 q8 y* W* |9 ^% v2 H0 v1 t: N/ w! X
不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。6 p' W B4 o/ g( y, p6 f7 S+ F5 [
# e2 b3 n% w/ c0 u! K
6 _8 o: v3 b+ }- _+ N
. _) V! `( P3 c( @6 m6 ~图1-1 MongoDB架构图
# U7 l3 I* J. z- Z5 Y: q$ {" I6 E h v0 U: Q3 n
MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。
1 e; M( P5 d5 L5 ]7 `) ?# [$ J6 a, G% w4 r
Shards
/ I0 n2 C# b3 x$ q4 A- W) c1 `! X
" p1 o( g4 N* x' s0 c8 Z2 O MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。
/ a2 l. P& j4 h. @8 y9 I8 L
. | {1 r0 G* N$ C3 q8 ]$ B$ T Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。
- e. P7 J7 A/ a' I
7 x' C. O$ ?4 r, c 每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。
! X$ R4 E8 s% n' b+ u0 C* l& @ D$ q! M0 l
如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。$ T; @! f* ]+ N$ N) W' ?2 \1 u. J
' P' {/ ?1 Q; `0 L8 X/ G o) X2 h/ ~+ c
Shard keys5 L1 N9 z$ F- c0 g6 {/ J6 o' \! Y
4 d+ O, {# {. G
为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。
6 g& T Q2 ~& g: Z6 h- N# P- h% x2 ]. J( m
如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,
" o+ V1 R5 \7 x" w& q8 t" ]6 j6 Z1 E9 P; u! ?, A' F) u
{9 ?& _& v8 T+ w, Y$ V
"ISBN": "987-30-3652-5130-82",
# D/ E2 ?! K( a% p7 ~ "Type": "CD",
. P5 \" V; H* H7 k; W9 S$ w' t "Author": "Nirvana",4 U! r& v9 D! i% r5 \. ?
"Title": "Nevermind",8 p* q. l K. X+ i9 p. I
"Genre": "Grunge",2 S( I& v' k! _
"Releasedate": "1991.09.24",2 I4 I. z, w! K, ?
"Tracklist": [- }2 K* e; }% u0 u- X# {
{: t. u$ m- y5 A5 D9 l
"Track" : "1",
& k4 K. @! N% k- J8 s0 { "Title" : "Smells like teen spirit",* ?+ ^( M$ l7 L2 I! `' A0 R
"Length" : "5:02"
: U7 x1 I ]6 `3 U6 B; N },
, R' \; K" K+ r6 A% Q, g4 D {
/ X7 r2 r/ E6 e0 \8 m" x1 Z6 t "Track" : "2",1 }5 R" n7 P8 A# S% y7 _
"Title" : "In Bloom",- p7 k3 N$ V7 z) }& b( K
"Length" : "4:15"
# L' r' z d* Y& f O. A/ Z5 G }7 L2 D2 P* f8 r5 g$ C3 ?
]
# `8 ~% |/ ~! D0 F* P6 x}
9 [& [2 j# ]* W: ~, Y4 ]' L- d8 `$ G. U2 T; i- T/ f; ?
{1 M* N( Z( f- Y3 q, B0 ?
"ISBN": "987-1-4302-3051-9",
& Y! r: ]+ [' ]9 Q "Type": "Book",
+ D& Q8 F( R7 V' z "Title": "Definite Guide to MongoDB: The NoSQL Database",
% t: `1 M& k; H5 \& c3 c "Publisher": "Apress",' q! \/ w/ a4 ~. D1 W4 w0 Z0 w
"Author": " Eelco Plugge",7 |+ z0 W0 M/ ^: O& b
"Releasedate": "2011.06.09"
$ Y; \. i: K% T& ~5 {: E& t! Q" V' N}
0 o# h' X0 H5 ~: U6 l. I# y/ n8 Z* |; X3 `
假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。
, A6 y9 z, [( E V
4 ^+ t1 ?; l3 g6 k9 X 在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。* `) X0 X# S' l$ _5 x
- B b+ _$ U( \3 O
例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。# j- z1 d" ?! w* V# U; J
: R! H. K2 q+ A+ W 很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。
+ z& d- ~# S7 d0 Q/ G9 s% a& a+ i) Z
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。
( w+ V& i l" n8 X- F2 m
! A4 Y3 p4 L( q% Q- BChunks
/ [, p* T+ ~7 A) B 7 W- ]# Q, X8 y# T& _' `
MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。
, g, y0 l% [0 v- |( {" j$ \
% B5 c- ^, o7 ~; W
& p; |- s2 ?$ r" f图1-2 chunk的三元组
+ a A3 U# @7 Z' g" F3 E
/ C: _9 y* O8 _, n1 E" S 其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。; I( ], M, W* t N( E) m6 D
i5 g; W6 R4 \2 L; _/ U$ { 如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。8 @- Z0 Z' o& w. @! s2 R
5 {: U8 n4 N7 g( @; G t4 h+ S1 m Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。& [( V& R( s- y2 ], Q h9 w
# I, s* Z, Q) M( s! s
一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。
0 n. m0 M; j: G# b- F
0 ?0 a' S/ O$ @ 当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。 \$ v/ M9 p |
, ^5 S# f0 z7 k9 o
这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。7 G, o5 I4 I% \. i2 Q8 `7 m9 v
5 b. s1 p9 T- S# D; N
Replica set, F4 Q; [1 L' p# d0 |' c2 h
+ R: F' j0 n: Y, x" _/ M2 C 在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。) P" s0 ]1 C: F
- b) K2 f7 K% e$ l/ G 这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。
v1 z8 x. r- }: o. h. O/ o N& Q" w; F6 ~
Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。4 d) x$ N, v$ d! D5 s s
( I( O+ O, L( W6 {" m# t2 A- d* i, F Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。
0 R4 l/ m2 z- V2 W( M* @5 B
4 c$ |; g! F x: d, J6 u wConfig Server9 q4 E/ E! M% h, j. K D) z
% G x& B5 K7 _ Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。
* ^( Z+ @. ~ W1 J3 n6 F- }/ e+ V% p1 @, w. e
每一个config server都包括了MongoDB中所有chunk的信息。
% r9 C. ~/ p8 \9 q3 p
( p; d- X5 @# }$ R Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。
% y- m# r9 r# g `* O
- [6 f: l+ q' O3 R7 H% f) Q+ r 如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。
# Q5 M7 ~& ?4 ^. X3 D
. ?7 Q! l' o: ?" v5 h6 R MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。6 L2 t7 A& A0 z7 `* H
. p& Q+ ?! i6 Y* H; h
Mongos; F. \) L7 B3 b. K* L
$ s/ P7 v8 D" v' l% [ 用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。
7 ~0 N, h- K0 A; @: t' N/ s! k1 Q& k% c6 G% |: C9 _( A9 h3 } [# K
当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。6 n; ~6 m5 k& S8 d: K7 u* n( ?
) Z" Z: J. N) X' }- q& l; y Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。! {: S b/ `* Y" ?6 ?
, K) ^6 i3 @) m* o) K6 G6 j0 m) R
Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。, R- o0 \* C f4 @2 W
7 b; \$ ~$ {) H; j6 R( }1 I8 B 通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。
- S, e) D. O5 X+ `" V; O* x" ~( g/ a7 K, j
- A- ?% S! k* K0 Q
Reference,
& k/ l [' I- L7 E' ?* S3 S* {$ ?4 O( S1 J
[0] Architectural Overview0 p' E# K: r3 ?+ I
http://www.mongodb.org/display/DOCS/Sharding+Introduction+ Y4 q! t3 a5 T3 Z
|
评分
-
查看全部评分
|