|
|
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。/ C0 K" j/ s3 L" z. ~
& c) K3 \3 j/ M& S: I( A0 w. U 阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?' |* `' ~- i1 j& Q) T; E1 K8 E
1 s( B) A- j m+ r
不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。
* }# X3 Q9 n+ X
. k1 ?7 }- q6 \
/ E: J8 c! M( A }
% F8 w$ ?" O P! d/ X2 z0 j/ d图1-1 MongoDB架构图
1 n+ {3 ?; o! F4 M7 [0 o
" u/ W. O/ C% o* K N; K# j MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。' M+ t$ V% E2 h5 \. g% s
" u& j, R7 I3 k5 ?" c& B" M
Shards4 P* N6 ?# U8 f
1 z' D+ i' [0 v' e
MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。$ {# y" h4 c. E0 ~3 S: X
6 L% x; u( C" L! b5 L7 T" v2 z
Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。" f4 d* B! e5 l5 T5 b6 R' p
+ g. K9 r- y4 u3 B+ u) N
每一个shard存储在一个物理服务器(server)上。Server上运行着mongod进程,通过这个进程,对shard中的数据进行操作,主要是增删改查。
3 A: p: a& m2 _, {" F) _) B% ]) h, ~" v& m; s$ D3 U" S
如果系统中的每个shard,只存储了一份数据,没有备份,那么当这个shard所在的server挂了,数据就丢失了。在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。0 c+ h7 ~ D+ g r4 K
" U( O! w7 r8 U5 n) g4 l
Shard keys2 X9 c0 v2 m. x* M1 w% A0 _# s
! u: V8 E0 g% [3 q: b2 n 为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。8 G5 R3 \( n3 b
7 `& J" B* Q. q) z 如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,
3 m, D$ z' W# C# ~$ d
* v. ` f5 A& r$ Y3 r- W& \, V{/ }, ], E1 C5 q
"ISBN": "987-30-3652-5130-82",6 c) f' M0 q( M$ N* y# l" }
"Type": "CD",5 P2 T& ] ?) u$ k1 t: A- b
"Author": "Nirvana",
6 ?, a" b0 N' @7 Q9 \ M* C "Title": "Nevermind",& v/ e! n2 T6 K" w/ s2 f
"Genre": "Grunge"," _1 s; b' X- r; r) j) b5 L$ w# V
"Releasedate": "1991.09.24",6 d# a, z" c3 Q; T2 V
"Tracklist": [
/ |' t& @7 ?/ i2 U D% F {
6 ^/ B v1 p" Z$ U "Track" : "1",
7 @( Q! N5 R" k- U: c3 F "Title" : "Smells like teen spirit",
, ~# {" t0 s% f/ o "Length" : "5:02"
! y, \2 G9 c- `! X) S) G0 g8 V: f },8 f% C7 @0 o& r" W
{" }3 E, @( z" |! B5 c
"Track" : "2",
: S; `' j, p3 h7 z# ~; o2 u1 ? Z "Title" : "In Bloom",
. Y% d6 ~% W* ? "Length" : "4:15"
; H2 P. N! r3 }9 J' N' A }
% U9 F: e. r* I0 s ]) C' \4 n! J' j. j% a8 W2 e* S" V/ A
}; X3 l0 S8 c& {/ n7 x
4 ?5 |7 T" A" q" ^& J" o% ^{
: H3 ?9 a9 |" C( l6 @ "ISBN": "987-1-4302-3051-9",8 c- e7 C8 W3 F& E! @/ r
"Type": "Book",
0 J3 T& d4 j- m0 V "Title": "Definite Guide to MongoDB: The NoSQL Database",! k6 t( b' Y# `
"Publisher": "Apress",
; j4 J2 c. ]7 |+ B3 G/ { "Author": " Eelco Plugge",9 J* A8 K% h8 j5 g/ W
"Releasedate": "2011.06.09"
: O5 X! i+ o$ Y+ P: R- z' p/ ~9 O} w1 B6 B- i! D% @
4 M$ c7 c) N' v0 j9 F \6 D 假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。
( r& j' t# a1 A0 P+ M, q+ ?1 b8 B0 \- A
在选择shard key的时候,一定要确保这个key能够把collection均匀地切分成很多chunks。9 ^3 p6 l: x) [3 H
- P( ?' y0 o# P- X# ]0 f 例如,如果我们选择“author”作为shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个chunk中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。
+ \/ _! p1 U* y8 T3 [5 Y7 k3 s7 X" z, j U+ H) }' h6 M
很多情况下,无论选择哪一个单一的 field 作为shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的shard key。9 p3 h# Q# b. n8 V0 X# E3 W
! @0 m" a$ R/ z# C! U5 w3 l& s
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上release-date,组成name-date的复合 shard key,例如“王二 2011”。3 C/ n: m" j- W
. c* F" t# q& F O0 P
Chunks
" B R: ^3 T) e- O1 d2 G # Y7 i/ p, _3 U
MongoDB按 shard key,把 collection切割成若干 chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。
) K# E V; j( @* @0 ? j& I% _3 b5 x; z' W+ V; N
3 k8 x8 F8 X$ E5 W. F: g: B" j" r
图1-2 chunk的三元组
2 C ^! S7 e1 H6 v7 D( E' J1 F
% Q4 a9 J9 n& f5 ?1 N4 G; u 其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。 x, w& C m5 Z9 J2 M% l
0 T0 E" T# ~+ b% R+ C 如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。
' ?2 S" m+ X: s
9 L8 h5 _7 _& J( O( _' F4 Y* d Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为order-preserving。) q k2 _8 Z5 C& x Z
! w2 o+ e$ S( K
一个 chunk最多能够存储64MB的数据。 当某个chunk存储的 documents包含的数据量,接近这个阈值时,一个chunk会被切分成两个新的chunks。0 m+ X* L& g( a4 M" V) p
) {2 f3 F, _* x/ D4 @* p
当一个shard存储了过多的chunks,这个shard中的某些chunks会被迁移到其它 shard中。
1 F% j* p* B9 H5 m, r3 d& b- g4 }+ L. N* ^: z9 o8 @
这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。/ y/ D2 E2 E5 {& W; J4 }! U
, ]& o- k3 V9 `' x
Replica set
! d; p: X% N! {5 L( N6 _+ O! _
, o+ @/ S9 `; Q: S. f 在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的servers,组成了一个replica set。
e! z9 _ e7 @* ]' `2 b
: U/ `5 V4 G( B2 Z: _5 P 这个replica set包括一个primary DB和多个secondary DBs。为了数据的一致性,所有的修改(insert / update / deletes) 请求都交给primary处理。处理结束之后,再异步地备份到其他secondary中。( I& [- x, I' e
& R# }: c# r u Primary DB由replica set中的所有servers,共同选举产生。当这个primaryDB server出错的时候,可以从replica set中重新选举一个新的primaryDB,从而避免了单点故障。# p8 g3 e. T5 R( e; z5 o
! T b) D( N; ]2 o7 _7 o- ]' j
Replica set的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。* a0 Z( F( K$ p! _( h7 E
. r! z- v& ~# G1 \5 ^Config Server. ]3 a+ S' ]# |6 \) x7 z
# w3 _1 V @/ r& O: v) c
Config servers用于存储MongoDB集群的元数据 metadata,这些元数据包括如下两个部分,每一个shard server包括哪些chunks,每个chunk存储了哪些 collections 的哪些 documents。$ t; D* m r7 G! v9 l
% c* U+ r; F4 e" M 每一个config server都包括了MongoDB中所有chunk的信息。, n( k2 p. W w! [* e
3 Y3 v+ ]0 Y4 R% ?* ]4 l( V
Config server也需要 replication。但是有趣的是,config server 采用了自己独特的replication模式,而没有沿用 replica set。
3 d! J+ {* Z k
- F, f, [. \9 ? 如果任何一台config server挂了,整个 config server 集群中,其它 config server变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。. K. a* y# D0 O% t) I" T4 n
0 K! m1 m. k/ Q$ {9 g! t3 s MongoDB的官方文档建议,配置3个config servers比较合适,既提供了足够的安全性,又避免了更多的config servers实例之间的数据同步,引起的元数据不一致的麻烦。
; x5 d7 e/ _2 r: ?1 t {3 C1 o) |7 q2 g9 |5 m6 i
Mongos
4 k+ E% U6 B! t$ w
8 J% {6 E" O5 S0 U1 [4 ?9 ? 用户使用MongoDB 时,用户的操作请求,全部由mongos来转发。. u. ~) }" v& d; E! i
; b4 {7 M5 _7 o- @- q' L) B: z 当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。; C! A. q. f) B% i% z3 i: t7 k
4 l! f( L2 @4 t u8 u- Y Mongos每次启动的时候,都要到config servers中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的mongos。) R) r3 [/ q2 C- s
2 u% j- |! v* o7 n* ]) ] Mongos之间,不存在彼此协同工作的问题。因此,MongoDB所需要配置的mongos server的数量,没有限制。, B* S; y5 g) ]& ] ^
# v5 n8 ^$ l/ ?+ q' B2 }
通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在replica set中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。5 }8 q4 j6 Q+ {; m0 u9 z) j" q
0 P* ~7 C% r: `) [. S3 r: r! k/ X: f
$ U1 s1 X7 |* {, H/ Z5 C/ l" b9 tReference, A4 x$ Q; I1 n: f1 e7 U
- \8 E; i; i9 F/ v6 M[0] Architectural Overview) p# u& I4 a* L5 E4 a- T& u
http://www.mongodb.org/display/DOCS/Sharding+Introduction
6 _/ w9 M. j# C- N! V" k- Z5 L |
评分
-
查看全部评分
|