By 盛楠、邓侃
关于MongoDB,我们能看到的资料,基本都是在指导大家如何使用MongoDB,但是,MongoDB内部是如何运作的,资料不是很多。
阅读使用手册,会有很多疑惑之处。例如,有人说,MongoDB 等同于分布式的 MySQL。它把一个Table ,按 row,分割成多个Shards,分别存放在不同的 Servers 上。这种说法是否正确?
不深入了解 MongoDB 的内部结构,就无法透彻地回答类似问题。这个系列文章,就来和大家探讨MongoDB的内部的工作方式。
图1-1 MongoDB架构图
MongoDB 通常运行在一个服务器集群上,而不是一个单机。图1-1,描述了一个MongoDB集群的基本组成部分,包括若干shards,至少一个config server,至少一个routing servers(又称 mongos)。
Shards
MongoDB的最基本的数据单元,叫document,类似于关系式数据库中的行 row。一系列documents,组成了一个collection,相当于关系式数据库中的table。当一个 collection 数据量太大时,可以把该collection按documents切分,分成多个数据块,每个数据块叫做一个chunk,多个chunks聚集在一起,组成了一个shard。
Sharding 的意义,不仅保障了数据库的扩容(scalability),同时也保障了系统的负载均衡(load balance)。
Shard keys
为了把collection切分成不同的chunks,从而存放到不同的shards中,我们需要制定一个切分的方式。
如前所述,在 MongoDB 数据库中,一个表collection由多个行 documents 组成,而每个 document,有多个属性 fields。同一个 collection 中的不同的 documents,可能会有不同的 fields。例如,有个 collection 叫 Media,包含两条 documents,
{
"ISBN": "987-30-3652-5130-82",
"Type": "CD",
"Author": "Nirvana",
"Title": "Nevermind",
"Genre": "Grunge",
"Releasedate": "1991.09.24",
"Tracklist": [
{
"Track" : "1",
"Title" : "Smells like teen spirit",
"Length" : "5:02"
},
{
"Track" : "2",
"Title" : "In Bloom",
"Length" : "4:15"
}
]
}
{
"ISBN": "987-1-4302-3051-9",
"Type": "Book",
"Title": "Definite Guide to MongoDB: The NoSQL Database",
"Publisher": "Apress",
"Author": " Eelco Plugge",
"Releasedate": "2011.06.09"
}
假如,在同一个 collection 中的所有 document,都包含某个共同的 field,例如前例中的“ISBN”,那么我们就可以按照这个 field 的值,来分割 collection。这个 field 的值,又称为 shard key。
在选择 shard key 的时候,一定要确保这个 key 能够把 collection 均匀地切分成很多 chunks。
例如,如果我们选择“author”作为 shard key,如果有大量的作者是重名的,那么就会有大量的数据聚集在同一个 chunk 中。当然,假设很少有作者同名同姓,那么“author”也可以作为一个shard key。换句话说,shard key 的选择,与使用场景密切相关。
很多情况下,无论选择哪一个单一的 field 作为 shard key,都无法均匀分割 collection。在这种情况下,我们可以考虑,用多个 fields,构成一个复合的 shard key。
延续前例,假如有很多作者同名同姓,他们都叫“王二”。用 author 作为 shard key,显然无法均匀切割 collection。这时我们可以加上 release-date,组成 name-date 的复合的 shard key,例如“王二 2011”。
Chunks
MongoDB 按 shard key,把 collection切割成若干chunks。每个 chunk 的数据结构,是一个三元组,{collection,minKey,maxKey},如图1-2 所示。
图1-2 chunk的三元组
其中,collection 是数据库中某一个表的名称,而 minKey 和 maxKey 是 shard key的范围。每一个 document 的shard key 的值,决定了这条document应该存放在哪个chunk中。
如果两条 documents 的 shard keys 的值很接近,这两条 documents 很可能被存放在同一个 chunk 中。
Shard key 的值的顺序,决定了 document 存放的 chunk。在 MongoDB 的文献中,这种切割 collection 的方式,称为 order-preserving。
一个 chunk 最多能够存储64MB的数据。 当某个 chunk 存储的 documents 包含的数据量,接近这个阈值时,一个 chunk 会被切分成两个新的 chunks。
当一个 shard 存储了过多的 chunks,这个shard中的某些 chunks 会被迁移到其它 shard 中。
这里有个问题,假如某一条 document 包含的数据量很大,超过 64MB,一个 chunk 存放不下,怎么办?在后续章节介绍 GridFS 时,我们会详细讨论。
Replica set
在生产环境中,为了保证数据不丢失,为了提高系统的可用性(availability),每一个shard被存储多份,每个备份所在的 servers,组成了一个 replica set。
这个 replica set 包括一个 primary DB 和多个secondary DBs。为了数据的一致性,所有的修改 (insert / update / deletes) 请求都交给 primary 处理。处理结束之后,再异步地备份到其他 secondary 中。
Primary DB 由 replica set中的所有 servers,共同选举产生。当这个 primaryDB server 出错的时候,可以从 replica set 中重新选举一个新的 primaryDB,从而避免了单点故障。
Replica set 的选举策略和数据同步机制,确保了系统的数据的一致性。后文详述。
Config Server
Config servers 用于存储 MongoDB 集群的元数据 metadata,这些元数据包括如下两个部分,每一个 shard server 包括哪些 chunks,每个 chunk 存储了哪些 collections 的哪些 documents。
每一个 config server 都包括了 MongoDB 中所有 chunk 的信息。
Config server 也需要 replication。但是有趣的是,config server 采用了自己独特的 replication 模式,而没有沿用 replica set。
如果任何一台 config server 挂了,整个 config server 集群中,其它 config server 变成只读状态。这样做的原因,是避免在系统不稳定的情况下,冒然对元数据做任何改动,导致在不同的 config servers 中,出现元数据不一致的情况。
MongoDB 的官方文档建议,配置 3 个 config servers 比较合适,既提供了足够的安全性,又避免了更多的 config servers 实例之间的数据同步,引起的元数据不一致的麻烦。
Mongos
用户使用MongoDB 时,用户的操作请求,全部由 mongos 来转发。
当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的 shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。
Mongos 每次启动的时候,都要到 config servers 中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的 mongos。
Mongos 之间,不存在彼此协同工作的问题。因此,MongoDB 所需要配置的 mongos server的数量,没有限制。
通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在 replica set 中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。
,出现元数据不一致的情况。
MongoDB 的官方文档建议,配置 3 个 config servers 比较合适,既提供了足够的安全性,又避免了更多的 config servers 实例之间的数据同步,引起的元数据不一致的麻烦。
Mongos
用户使用MongoDB 时,用户的操作请求,全部由 mongos 来转发。
当 mongos 接收到用户请求时,它先查询 config server,找到存放相应数据的 shard servers。然后把用户请求,转发到这些 shard servers。当这些 shard servers完成操作后,它们把结果分别返回给 mongos。而当 mongos 汇总了所有的结果后,它把结果返回给用户。
Mongos 每次启动的时候,都要到 config servers 中读取元数据,并缓存在本地。每当 config server中的元数据有改动,它都会通知所有的 mongos。
Mongos 之间,不存在彼此协同工作的问题。因此,MongoDB 所需要配置的 mongos server的数量,没有限制。
通过以上的介绍,我们对每个组成部分都有了基本的了解,但是涉及到工作的细节,我们尚有诸多疑问,例如,一个chunk的数据太大,如何切分?一个shard数据太多,如何迁移?在 replica set 中,如何选择primary?server挂了,怎么进行故障恢复?接下来的章节,我们逐个回答这些问题。