前面几篇文章的分析复制集解决了数据库的备份与自动故障转移,但是围绕数据库的业务中当前还有两个方面的问题变得越来越重要。一是海量数据如何存储?二是如何高效的读写海量数据?尽管复制集也可以实现读写分析,如在primary节点上写,在secondary节点上读,但在这种方式下客户端读出来的数据有可能不是最新的,因为primary节点到secondary节点间的数据同步会带来一定延迟,而且这种方式也不能处理大量数据。mongoDB从设计之初就考虑了上面所提到的两个问题,引入了分片机制,实现了海量数据的分布式存储与高效的读写分离。复制集中的每个成员是一个mongod实例,但在分片部署上,每一个片可能就是一个复制集。
上面谈到了分片的优点,但分片的使用,会使数据库系统变得复杂。什么时候使用分片也是需要考虑的问题。mongoDB使用内存映射文件的方式来读写数据库,对内存的管理由操作系统来负责,随着运行时间的推移,数据库的索引和数据文件会变得越来越大,对于单节点的机器来说,迟早会突破内存的限制,当磁盘上的数据文件和索引远大于内存的大小时,这个时候操作系统会频繁的进行内存交换,导致整个数据库系统的读写性能下降。因此对于大数据的处理,要时刻监控mongoDB的磁盘I/O性能、可用内存的大小,在数据库内存使用率达到一定程度时就要考虑分片了。通过分片使整个数据库分布在各个片上,每个片拥有数据库的一部分数据,从而降低内存使用率,提高读写性能。
分片部署架构
下面看看一个具体的分片部署架构是什么,为了后续的研究,先部署一个分片集群。
分片集群主要由mongos路由进程、复制集组成的片shards、一组配置服务器Configure构成,下面对这些模块一一解释。
分片集群中的一个片shard实际上就是一个复制集,当然一个片也可以是单个mongod实例,只是在分片集群的生产环境中,每个片只是保存整个数据库数据的一部分,如果这部分数据丢失了,那么整个数据库就不完整了,因此应该保证每个片上的数据稳定性和完整性,通过第7章对复制集的分析,复制集能够达到这样的要求。因此通过将片配置为复制集的形式,使片shard在默认情况下读写都在复制集的primary节点上,每个片同时具有自动故障转移、冗余备份的功能,总之复制集所具有的的特性在片上都能得到体现。
mongos路由进程是一个轻量级且非持久性的进程。轻量级表示它不会保存任何数据库中的数据,它只是将整个分片集群看成一个整体,使分片集群对整个客户端程序来说是透明的,当客户端发起读写操作时,由mongos路由进程将该操作路由到具体的片上进行;为了实现对读写请求的路由,mongos进程必须知道整个分片集群上所有数据库的分片情况即元信息,这些信息是从配置服务器上同步过来的,每次进程启动时都会从configure服务器上读元信息,mongos并非持久化保存这些信息。
配置服务器configure在整个分片集群中想到重要,上面说到mongos会从配置服务器同步元信息,因此配置服务器要能实现这些元信息的持久化。配置服务器上的数据如果丢失,那么整个分片集群就无法使用,因此在生产环境中通常利用三台配置服务器来实现冗余备份,这三台服务器是独立的,并不是复制集架构。
下面按照上图描述来配置一个这样的分片集群。
(1)配置复制集rs0并启动,参考前面关于复制集中介绍的6个步骤。
先创建好rs0中各节点的数据文件存放路径、日志文件路径以及配置文件,其中配置文件的内容如下:
rs0中primary节点的配置文件为rs0_0.conf:
dbpath = E:\mongodb-win32-i386-2.4.3\db_rs0\data\rs0_0
logpath = E:\mongodb-win32-i386-2.4.3\db_rs0\logs\rs0_0.log
journal = true
port = 40000
replSet = rs0
rs0中secondary节点的配置文件为rs0_1.conf:
dbpath = E:\mongodb-win32-i386-2.4.3\db_rs0\data\rs0_1
logpath = E:\mongodb-win32-i386-2.4.3\db_rs0\logs\rs0_1.log
journal = true
port = 40001
replSet = rs0
rs0中arbiter节点的配置文件为rs0_2.conf:
dbpath = E:\mongodb-win32-i386-2.4.3\db_rs0\data\rs0_2
logpath = E:\mongodb-win32-i386-2.4.3\db_rs0\logs\rs0_2.log
journal = true
port = 40002
replSet = rs0
按照7.1节介绍的步骤启动复制集rs0。
(2)配置复制集rs1并启动,步骤与上面相同,这里给出rs2中各节点对应的配置文件内容。
rs1中primary节点的配置文件为rs1_0.conf:
dbpath = E:\mongodb-win32-i386-2.4.3\db_rs1\data\rs1_0
logpath = E:\mongodb-win32-i386-2.4.3\db_rs1\logs\rs1_0.log
journal = true
port = 40003
replSet = rs1
rs1中primary节点的配置文件为rs1_1.conf:
dbpath = E:\mongodb-win32-i386-2.4.3\db_rs1\data\rs1_1
logpath = E:\mongodb-win32-i386-2.4.3\db_rs1\logs\rs1_1.log
journal = true
port = 40004
replSet = rs1
rs1中primary节点的配置文件为rs1_2.conf:
dbpath = E:\mongodb-win32-i386-2.4.3\db_rs1\data\rs1_2
logpath = E:\mongodb-win32-i386-2.4.3\db_rs1\logs\rs1_2.log
journal = true
port = 40005
replSet = rs1
按照7.1节介绍的步骤启动复制集rs0
通过rs.status()检查并确认上述复制集已启动且配置正确。
(3)配置configure服务器
configure服务器也是一个mongod进程,它与普通的mongod实例没有本质区别,只是它上面的数据库以及集合是特意给分片集群用的,其内容会在后面详细介绍。三个独立的配置服务器对应的启动配置文件内容如下:
configure服务器1的配置文件cfgserver_0.conf:
dbpath = E:\mongodb-win32-i386-2.4.3\db_configs\data\db_config0
logpath = E:\mongodb-win32-i386-2.4.3\db_configs\logs\db_config0.log
journal = true
port = 40006
configsvr = true
configure服务器2的配置文件cfgserver_1.conf:
dbpath = E:\mongodb-win32-i386-2.4.3\db_configs\data\db_config1
logpath = E:\mongodb-win32-i386-2.4.3\db_configs\logs\db_config1.log
journal = true
port = 40007
configsvr = true
configure服务器3的配置文件cfgserver_2.conf:
dbpath = E:\mongodb-win32-i386-2.4.3\db_configs\data\db_config2
logpath = E:\mongodb-win32-i386-2.4.3\db_configs\logs\db_config2.log
journal = true
port = 40008
configsvr = true
配置服务器上的mongod实例启动时的配置选项与普通的mongod实例差不多,这里只是多了一个configsvr=true的选择,说明这个mongod实例是一个configure类型的mongod实例。
启动上面三个配置服务器:
>mongod --config E:\mongodb-win32-i386-2.4.3\configs_cfgservers\cfgserver_0.conf
>mongod --config E:\mongodb-win32-i386-2.4.3\configs_cfgservers\cfgserver_1.conf
>mongod --config E:\mongodb-win32-i386-2.4.3\configs_cfgservers\cfgserver_2.conf
(4)配置mongos路由服务器
其配置文件cfg_mongos.conf内容为:
logpath = E:\mongodb-win32-i386-2.4.3\mongos\logs\mongos.log
port = 40009
configdb = Guo:40006,GuO:40007,GuO:40008
启动路由服务器:
>mongos --config E:\mongodb-win32-i386-2.4.3\mongos\cfg_mongos.conf
实例对应的进程为mongos,路由服务器只是一个轻量级和非持久化操作的进程,因此上面的配置文件里面没有像其它mongod实例那样有一个存放数据文件的路径选项dbpath。
(5)添加各分片到集群
上面已经完成了两个片(复制集)、三个配置服务器、一个路由服务器且它已经知道从哪些配置服务器上同步元数据(configdb = Guo:40006,GuO:40007,GuO:40008),接下来要做的是将各个片添加到集群中。
打开一个mongo客户端连接到mongos服务器:
>mongo --port 40009
添加两个分片
mongos> sh.addShard("rs0/GUO:40000,GUO:40001")
{ "shardAdded" : "rs0", "ok" : 1 }
mongos> sh.addShard("rs1/GUO:40003,GUO:40004")
{ "shardAdded" : "rs1", "ok" : 1 }
这里添加分片的命令是sh.addShard(),参数是复制集名以及复制集中不包含arbiter类型的所有节点。
(6)最后通过命令sh.status()检查上面的配置是否正确,正常的话输出信息类似下面:
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"version" : 3,
"minCompatibleVersion" : 3,
"currentVersion" : 4,
"clusterId" : ObjectId("521b11e0a663075416070c04")
}
shards:
{ "_id" : "rs0", "host" : "rs0/Guo:40000,Guo:40001" }
{ "_id" : "rs1", "host" : "rs1/Guo:40003,Guo:40004" }
databases:
{ "_id" : "admin", "partitioned" : false, "primary" : "config" }
上面输出的信息中clusterId字段表示此分片集群的唯一标示;shards为分片集群中包含的所有片,其中_id为此片的名称,host为片中的主机的host信息;databases为集群中的所有数据库,其中_id为数据库名称,partitioned表示此数据库是否支持分片;primary表示当数据库支持分片,此数据库上所有未分片的集合所在的片。
此时在整个分片集群中还没有创建任何其它数据库,通过路由进程mongos连接集群,执行命令show dbs可以看到集群中只有系统默认创建的一个config数据库,且这个数据库只存在于三个配置服务器上,config数据库中的集合包含了整个集群的配置信息,执行命令show collections,可以看到有如下集合:
mongos> show collections
changelog:保存被分片的集合的任何元数据的改变,例如chunks的迁移、分割等。
Chunks:保存集群中分片集合的所有块的信息,包含块的数据范围与块所在的片。
Databases:保存集群中的所有数据库,包含分片与未分片的。
Lockpings:保存跟踪集群中的激活组件。
locks:均衡器balancer执行时会生产锁,在此集合中插入一条记录。
mongos:保存了集群中所有路由mongos的信息。
Settings:保存分片集群的配置信息,如每个chunk的大小(64MB)、均衡器的状态。
Shards:保存了集群中的所有片的信息。
system.indexes:保存config数据库中的所有索引信息。
Version:保存当前所有元信息的版本。
由上所述得知,配置服务器中的config数据库的信息对于整个集群来说是至关重要的,这也是生产环境中最少需要3个配置服务器做冗余备份的原因;同时上面对config数据库的所有操作,都是通过客户端连接mongos后再进行的,尽管config数据库也是在单个mongod实例上,我们可以直接通过客户端连接到这个实例,然后做操作,但是这样会出现配置服务器上的信息不一致的风险,因此我们队集群的所有操作应该是通过客户端连接mongos来执行。
最好探讨一下实际部署的问题,通过上图和前面的分析可知,一个生产环境的最少需要9个mongod实例进程,一个mongos进程实例,理论上说最少需要10台机器才能组成。但是这些进程中有些并不需要很多软硬件资源,它们可以与其它进程共存部署在同一个机器上,如复制集中arbiter进程、mongos进程可以部署到应用程序所在的服务器,综合考虑后可以得到下面一个典型的部署:
图8.2
上图部署的总体原则是使每一个片(复制集)中的primary节点、secondary节点、arbiter节点分开以及三台配置服务器分开,当图中的四台机器任何一台宕机后,集群都能够正常运行。