|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。
+ H6 i5 ~/ F2 }! ]0 W9 x7 z+ b% e- {8 F3 p0 o
阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?
3 I4 J' H! u3 w' F0 i3 u, {) F# Q
2 c, e5 r: }: ~5 R' z1 \' l 不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。0 b3 y [4 }* `' v0 q% U- z' @
# D9 ^& Q6 o/ f5 o. ]: f2 D0 ^* Y
4 F' m0 z1 c: W Q1 _
. u% s4 |, E) a0 v图1-1 MongoDB架构图
7 u$ |1 e0 @/ X- j% r6 G A! W* v! E6 I' O2 p- g- X' @
MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。
1 i, k% _! y2 v: a8 Y4 m9 j3 O3 y' Y6 r0 O, r& g9 U' X; |
Shards
( Z5 Z0 g% i, R. L6 \5 m& H* P8 Q! u: B
MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。# J8 p- T8 V4 X9 `
+ M: } l- q2 A% X9 v7 c6 ^$ P Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。* u$ W0 `9 \" l! T& S
* v$ H% j" t9 U' B7 Y 每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。
c8 {! s \$ }# o* Y0 Z0 m3 k2 w7 h9 A. c1 {/ n! j
如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
5 F! \! p% v% l- Y3 ?5 j% B
" u J/ S1 S' M4 b8 oShard keys. J- A& N4 ?8 R6 t( z9 x- ~7 T& I
3 ]- t, o$ ~" K
为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。- Z! _0 B2 i6 g- n( P7 A
+ Z, x% p1 M, X1 i, i. R 如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,7 e1 I7 J! z' D* u
( x5 a/ x" |: p" ^3 t
{: h; D5 ~9 Z) D" H. @1 Q/ @
"ISBN": "987-30-3652-5130-82",
+ w- G/ G7 P+ `" A* r! @ "Type": "CD",% K3 j$ K8 `. D) h4 p# [4 a
"Author": "Nirvana",# ~8 |1 y- T* m# {6 g" O
"Title": "Nevermind",8 ]" `# E5 w( t! P6 p$ W
"Genre": "Grunge",% R, l; n1 {6 M/ _5 E1 V6 @ y
"Releasedate": "1991.09.24",
' P3 J8 E" g) @9 E/ K5 L "Tracklist": [
$ h. [0 I) h$ ^ e" a+ n4 N# x7 \" u {
: j7 n' ^" [( A& t$ z6 e4 c1 l "Track" : "1",8 w1 q% n! Q0 }
"Title" : "Smells like teen spirit",
. G; R, l3 y1 @' o4 O "Length" : "5:02"
: H0 N, g4 a+ J/ q+ a, m; d },$ G$ Y, r6 f# G l6 y B/ n
{
% I: ]8 |7 T9 d. s3 A! F s+ h "Track" : "2",$ @. E, O I2 w$ o5 _' X8 l
"Title" : "In Bloom",
* z& k; P3 a: w3 Y9 C "Length" : "4:15"
: M5 k2 P0 b8 ^% j) `9 y }
. R" U. z5 ^ x! i3 H ]3 F- M( K: [- ~6 F9 w
}8 ]. R6 Y0 v( {2 G; N; p* b
/ R. P3 o% @: {" u9 O; x{
& i- V/ L' |" U7 J/ M "ISBN": "987-1-4302-3051-9",& I* X# N# |- p2 M7 H9 _' R, n
"Type": "Book",- l6 { T4 D& A( O" d
"Title": "Definite Guide to MongoDB: The NoSQL Database",
" C+ D8 O+ M% b$ M- ~% D "Publisher": "Apress",
2 L, S* ?% R: d. C: r6 g5 L "Author": " Eelco Plugge",4 @) f0 ^, r0 s4 w: a
"Releasedate": "2011.06.09"
3 M0 r* m: M3 Y7 s. k}/ I a" }9 t n3 @$ y5 ]
' p; R( g9 A+ X, g
假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。# n9 J& q3 `; f1 i
7 L6 U$ C/ a, T9 @8 H1 P 在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。, X6 x9 O0 r( j1 h# A/ D
" i$ Y5 h; q" x" \5 t1 L# d: e/ P( U
例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。: k4 f' o: N* g
& d/ Z! q( m, d: n+ [. Y
很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。
1 c$ T; X7 }) H9 |. @, D0 d) r) N8 U2 E2 W8 U6 v$ F" [8 `
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。 O4 b1 F" r, o
/ k/ h# _ I! o6 @
Chunks
R! ~; @& Q1 ?% ?* c o 8 x# c8 U6 ^( b: B9 l
MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。1 S& i: G9 k$ W$ c3 q+ T3 F' i
9 [* y& M$ a% w9 m
% w9 d* [2 l& J3 _5 m- {图1-2 chunk的三元组 / T% V% \ l& h- |4 T, T
. p2 Q4 o8 x8 l 其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。$ c4 F& N e: v( Q; i" L1 l+ x
: t6 W+ c9 w. P- `& h+ m7 M- ^$ t3 B! v
如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。
9 v( ^; p( D. G, X6 _
# F3 ^ C/ I- B& k- m" n9 ? Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。
. Z: R% f3 e4 b+ j* ~! R
7 V- h0 X4 K# k7 t- V4 a$ m5 P3 X- h$ O 一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。& Y) s) I% {% ?7 {: J6 A
4 t7 H1 C o# K" T* D e1 P2 L
当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。% B! k6 l0 H1 y4 @5 v# F% }
$ w6 b1 M# @) U8 {4 g 这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。1 ~8 p0 P% t0 Q: b0 Z9 l6 _2 i' w$ t0 Z
! v. ^) @7 p$ i, W. L# x6 ]+ jReplica set$ q6 @$ e' Q1 ^# ~+ ~; X
: u6 A4 E X, z' t- @" ]
在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
" l* M( T# D2 i. j
( D- e+ [8 t+ ]; K: h* I# n3 Z0 Y9 y8 R 这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。
" o8 p9 O F) U9 Y- q
$ G( S; j& e% ^; o; k Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。
$ M. n" f) K) K2 M# O1 }8 }, s; B8 n0 p% H& q8 {2 {* P0 W
Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。9 z# ^: R- X! u& O t! z, ^3 ?
2 [2 z# R9 d7 R- ~! Y8 zConfig Server& |) w: @" G2 W$ K
& k9 i: Q3 D3 L
Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。
( Z7 ^; G* Y1 }9 W: G( \( Q4 Z7 N0 v7 I
每一个config server都包括了MongoDB中所有chunk的信息。" W: f$ S- y. B: @+ o
: U* ^, T% J5 H4 w \! j Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。0 k! k/ F/ `/ j- n+ g6 n
2 Y: r; `/ B& j# i6 V
如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。) ?* ] [( O) Q5 d9 G$ O
: R8 _8 {# p7 v" _ MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。
5 p" z( o A" `2 K6 u7 P# Q5 ], J0 C- W* `: P9 }
Mongos
+ B: s: |6 L7 e9 `" R1 X9 \! ^/ r# Z' g1 y$ l! Q$ ?
用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。# d: j8 A: g- ]0 _! K4 o( |
! W G8 K. s0 T 当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。! ~$ P# x; ~% a3 f
6 d( y5 t* i5 Z Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。+ j( {- ^' M# [
- u# M% {# M4 E# @" n1 Y h8 x Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。+ _1 U" n D, X* C2 Y7 m
) ~' _9 O# n; v& ]& b2 p 通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。; z6 r$ g( R4 V$ Y8 f) t
4 @% [9 q& o0 J+ o2 X
+ @9 W: `5 d/ ?: q# ?, d9 {- Q* E
Reference,0 H* J8 [! Q2 g! ^( }' B
& z. L$ ^, o$ o, q0 F, G% n[0] Architectural Overview
: P1 }! i' }9 o& n7 u% s6 ihttp://www.mongodb.org/display/DOCS/Sharding+Introduction
# B: d6 D- w6 v! e |
评分
-
查看全部评分
|