MongoDB的分片和副本集--部署
Mongodb的分片+replicaset
一、 mongodb的分片
分片(shard)是集群中负责数据某一子集的一台或多台服务器。一个分片可由多台服务器组成。
分片是指不同的服务器保存不同的数据,它们的数据总和就是整个数据集。
为了在分片间均匀地分配数据,MongoDB会在不同分片间移动数据子集。它会根据片键(key)来决定移动哪些数据。
1. 分片数据
1.1 一分片一区间
分片最简单的方式就是每个分片负责存储一个区间段的数据,如用户名由4个分片进行存储,由用户名作为分片的依据那么将是如下情况:
图:对用户名数据的分片存储
这种方式有一个问题,在数据迁移时,可能会造成级联效应,即如果要将第一分片中的数据均衡,可能要影响很多分片,造成移动的数据量过大,影响整个系统的正常运行。原因很简单,每个分片要保持一区间模式。
1.2 一分片多区间
这种方式可以有效的避免一分片一区间的数据迁移问题,宗旨是每个分片可以存储多个区间的数据。例子,如果[a, f)和[f, n)区间的数据比后面的两个分片都大,需要进行数据移动,则可以将[a, f)分为[a, c)和[c, f),[f, n)分为[f, j)和[j, n),并进行数据移动,则最终成为下面的分片状态:
图:数据移动后的状态
在添加新分片后,MongoDB可以从每个分片取出部分数据,移动到新分片上。
图:新分片加入后的数据迁移
2. 创建块
在决定分配数据时,必须为块区间选择一个键,这个键叫做片键(shard key),片键可以是任意字段或是字段组合。
在初始情况下,MongoDB只会创建一个块,这个块的区间是覆盖了所有数据的区间,当数据足够多时,才会发生分片。
在分片配置后,需要指定shard key,通过命令sh.shardCollection来设置。
3. 平衡
如果存在多个可用的分片,只要块的数量足够多,MongoDB就会把数据迁移到其他分片上。这个迁移过程叫做平衡,由平衡器进程负载执行。
平衡器的触发条件:一个分片必须比块最少的分片多出至少9个块才会被触发。这时块会被迁移出拥挤的分片,直到其他分片平衡为止。
平衡器并不很敏感,否则,分片间的稍微不平衡就会引起平衡操作,对系统的资源浪费过多。
一些方便验证配置的方法:
(1) 可以通过设置chunkSize来指定块的大小,以此可以快速的看出数据迁移的过程,如:--chunkSize 1指块的大小为1MB,这样,在插入10MB数据后,就会有迁移发生。
(2) 可以设置递增块大小,这样对于前十几个块,MongoDB会特意自动降低块的大小,从200MB降到64MB。
4. mongos
mongos是用户和集群间的交互点,它向用户提供了一个集群的唯一入口,将复杂的处理流程隐藏起来。即,对集群的所有操作都发往mongos服务上,而mongos会将所有用户的请求转发到对应的分片上。
5. 集群中的角色
MongoDB Sharding Cluster是一种可以水平扩展的模式,在数据量很大时特给力,一直想研究一下,要构建一个MongoDB Sharding Cluster,需要三种角色:
Shard Server: mongod实例,用于存储实际的数据块,实际生产环境中一个Shard Server角色可由几台机器组个一个Replica Set承担,防止主机单点故障。
Config Server: mongod实例,存储了整个Cluster Metadata,其中包括Chunk信息。
Route Server: mongos实例,前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。
集群的架构如下:
图:分片集群的架构
图:副本集分片集群的架构
二、 副本集
1. 副本集(replica set)
MongoDB的replica set是一个mongod进程实例簇,数据在这个簇中相互复制,并自动进行故障切换。
MongoDB的数据库复制增加了冗余,确保了高可用性,简化了管理任务如备份,并且增加了读能力。大多数产品部署都使用了复制。MongoDB中primary处理写操作,其它进行复制的成员则是secondaries。
2. 成员角色
成员可以是以下某种角色:
|
成为primary |
对客户端可见 |
参与投票 |
延迟同步 |
复制数据 |
Default |
√ |
√ |
√ |
∕ |
√ |
Secondary-Only |
∕ |
√ |
√ |
∕ |
√ |
Hidden |
∕ |
∕ |
√ |
∕ |
√ |
Delayed |
∕ |
√ |
√ |
√ |
√ |
Arbiters |
∕ |
∕ |
√ |
∕ |
∕ |
Non-Voting |
√ |
√ |
∕ |
∕ |
√ |
表:副本集角色属性
3. 故障切换恢复
副本集能够自动进行故障切换恢复。如果primary掉线或者无反应且多数的副本集成员能够相互连接,则选出一个新的primary。
在多数情况下,当primary宕机、不可用或者是不适合做primary时,在没有管理者干预的几秒后会进行故障切换。
如果MongoDB部署没有如预期那样进行故障切换,则可能是下面的问题:
- 剩余的成员个数少于副本集的一半
- 没有成员有资格成为primary
4. Rollback
多数情况下,回滚操作可以优雅的对不能进行故障切换恢复的情况进行恢复。
Rollbacks操作发生在primary处理写操作,但其它成员没有成功的进行复制之前primary掉线时。当先前的primary开始复制时,则表现出rollback。如果操作复制到其它成员,该成员可用,并且可以和大多数的副本集连接,则没有rollback。
Rollbacks删除了那些没有进行复制的操作,以保证数据集的一致性。
5. 选举(Elections)
当任意的故障切换发生,都会伴随着一个选举的出现,以此来决定哪个成员成为primary。
选举提供了一种机制,用于副本集中的成员无需管理员的干预,自动的选出一个新的primary。选举可以让副本集快速和坚决的从故障中恢复。
当primary变为不可达时,secondary成员发起选举,第一个收到大多数选票的成员成为新的primary。
6. 成员优先级
在副本集中,每个成员都有优先级,它可以帮助决定选举出primary。默认情况下,所有的成员的优先级都为1。
7. 一致性
在MongoDB中,所有针对于primary的读操作都与最后的写操作结果相一致。
如果客户端配置了读选项以允许secondary读,读操作能从没有近期复制更新或操作的secondary成员返回结果。在这种情况下,查询操作可能返回之前的状态。
这种行为有时称为最终一致性,因为secodary成员的状态最终都会是primary的状态。MongoDB不能保证从secondary成员的读操作的强一致性。
没有办法保证从secondary成员读的一致性,除非在配置时保证写操作成功的在所有节点上都执行成功后才返回成功。
三、 副本集配置
三成员副本集为大多数网络分区和系统故障提供了足够的冗余。另外,这些集合有足够的分布式读操作能力。大多数部署不需要额外的成员或配置。
有三个服务器:
Server1 : 218.30.117.193 Server2 : 218.30.117.195 Server3 : 218.30.117.196
1. 需求
大多数副本集由三个或以上各mongod实例。这里描述了一个三成员副本集。生产环境下应该至少有三个独立的系统,每个系统上运行一个单独的mongod实例。在开发系统下,你能在一个本地系统或者虚拟系统下运行三个mongod实例。在生产环境下,要尽可能的将成员分隔开来。举个例子,当使用虚拟机进行生产部署时,每个成员运行在不同的单独主服务器上,有冗余的电路和网络路径。
2. 部署一个开发测试副本集
这个例子中创建了一个名为rs0的副本集。
(1) 在创建副本集之前,确认每个成员都能成功的连接到其它成员上。网络配置必须允许任意成员之间的连接。
(2) 运行作为rs0副本集的成员的三个mongod实例。
3. 创建必要的数据目录,命令如下:
mkdir –p /srv/mongodb/rs0-0 /srv/mongodb/rs0-1 /srv/mongodb/rs0-2
4. 在不同的窗口下执行下面的命令:
mongod --port 27017 --dbpath /srv/mongodb/rs0-0 --replSet rs0 --smallfiles --oplogSize 128 mongod --port 27018 --dbpath /srv/mongodb/rs0-1 --replSet rs0 --smallfiles --oplogSize 128 mongod --port 27019 --dbpath /srv/mongodb/rs0-2 --replSet rs0 --smallfiles --oplogSize 128
这运行了每个rs0副本集成员的实例,运行在不同的端口。如果你已经使用了这些端口,则可以使用其他的端口。
每个mongod测试使用的--smallfiles和--oplogSize选项降低了磁盘空间。
重要:在没有另外声明时,这些选项只用于测试配置。
(3) 打开一个mongo shell并连接第一个mongod实例,命令如下:、
mongo --port 27017
(4) 在mongo shell环境中创建一个副本集配置对象,用于初始化副本集,命令如下:
rsconf = { _id: "rs0", members: [ { _id: 0, host: "<hostname>:27017" } ] }
(5) 使用rs.initiate()命令初始化由当前成员组成的副本集,并使用默认配置:
rs.initiate( rsconf )
(6) 显示当前副本集的配置
rs.conf()
(7) 通过rs.add()命令向副本集中添加第二和第三个mongod实例。用你的主机名替换下例中的<hostname>:
rs.add("<hostname>:27018") rs.add("<hostname>:27019")
在这些命令后,将返回一个功能完整的副本集,新的副本集会在几秒内选举出主成员。
(8) 通过rs.status()命令随时检查你的副本集的状态
5. 部署一个生产副本集
部署一个生产副本集与开发测试副本集很相似,但有以下不同:
◆ 每个副本集成员都运行在单独的机器上,并且MongoDB进程都绑定端口27017。
◆ 每个副本集成员都必须通过DNS解析可达或者主机名如下:
mongodb0.example.net
mongodb1.example.net
mongodb2.example.net
配置适当的DNS名或设置系统的/etc/hosts文件以反映配置。
◆ 你在每个系统指定一个运行时的配置以一个配置文件的形式,存放在/etc/mongodb.conf或者一个相关的位置。你不能通过命令行选项指定一个运行时配置。
对于每个mongoDB实例,使用下面的配置。针对于你的系统设置适当的配置参数:
port = 27017 bind_ip = 10.8.0.10 dbpath = /srv/mongodb/ fork = true replSet = rs0
你不一定需要指定bind_ip接口。然而,如果你不知道一个接口,则MongoDB会监听所有的可用IPv4的接口上的连接。修改bind_ip反映出系统上的安全接口,它能够访问其他的集合成员,并且其他副本集成员也能访问当前成员。DNS或主机名必须指定并解析成IP地址。
(1) 在创建你的副本集之前,确认每个成员都能成功的连接到其它成员上。网络配置必须允许任意成员之间的连接。
(2) 每个运行mongod进程的系统上执行下面的命令:
mongod --config /etc/mongodb.conf
(3) 启动一个mongo shell连接这个主机
mongo
(4) 使用rs.initiate()命令初始化由当前成员组成的副本集,并使用默认配置
rs.initiate()
(5) 显示当前副本集配置
rs.conf()
(6) 向副本集中增加两个成员,命令如下:
rs.add("mongodb1.example.net") rs.add("mongodb2.example.net")
在这些命令后,将返回一个功能完整的副本集,新的副本集会在几秒内选举出主成员。
(7) 通过rs.status()命令随时检查你的副本集的状态。
四、 副本集部署
- 启动mongod
在每台运行mongod服务的机器上增加配置文件/etc/mongodb-rs.conf,内容为:
port = 27017 dbpath = /usr/tmp/mongodb logpath = /usr/tmp/mongodb/log.log fork = true replSet = rs0
并创建/usr/tmp/mongodb目录
通过下面命令启动mongod:
/usr/local/mongodb/bin/mongod -f /etc/mongodb-rc.conf
1. 修改每个机器的/etc/hosts
在每台机器的/etc/hosts文件中增加:
218.30.117.193 mongodb1.example.net 218.30.117.195 mongodb2.example.net 218.30.117.196 mongodb3.example.net
2. 使用mongo shell连接mongod,进行配置
/usr/local/mongodb/bin/mongo 218.30.117.193 MongoDB shell version: 2.4.2 connecting to: 218.30.117.193/test > config = {_id:'rs0', members: [ ... {_id:0, host:'mongodb1.example.net'}, ... {_id:1, host:'mongodb2.example.net'}, ... {_id:2, host:'mongodb3.example.net'}]} > rs.initiate(config) >rs.status()
可以看到当前副本集的状态。
3. 故障切换
假设193为Primary,其它为Secondary,则可以使用mongo shell连接195,看到下面结果
/usr/local/mongodb/bin/mongo 218.30.117.195 MongoDB shell version: 2.4.2 connecting to: 218.30.117.195/test rs0:SECONDARY> rs.isMaster() { "setName" : "rs0", "ismaster" : false, "secondary" : true, "hosts" : [ "mongodb2.example.net:27017", "mongodb3.example.net:27017", "mongodb1.example.net:27017" ], "primary" : "mongodb1.example.net:27017", "me" : "mongodb2.example.net:27017", "maxBsonObjectSize" : 16777216, "maxMessageSizeBytes" : 48000000, "localTime" : ISODate("2013-05-15T06:14:01.153Z"), "ok" : 1 }
同样可以登录196查看其状态。
我们停止193的mongod,然后发现已经mongo shell连接不上,而通过另外两个成员可以看到副本集的状态:
/usr/local/mongodb/bin/mongo 218.30.117.195 MongoDB shell version: 2.4.2 connecting to: 218.30.117.195/test rs0:PRIMARY> rs.status() { "set" : "rs0", "date" : ISODate("2013-05-15T06:16:03Z"), "myState" : 1, "members" : [ { "_id" : 0, "name" : "mongodb1.example.net:27017", "health" : 0, "state" : 8, "stateStr" : "(not reachable/healthy)", "uptime" : 0, "optime" : { "t" : 1368626414, "i" : 1 }, "optimeDate" : ISODate("2013-05-15T14:00:14Z"), "lastHeartbeat" : ISODate("2013-05-15T06:16:02Z"), "lastHeartbeatRecv" : ISODate("1970-01-01T00:00:00Z"), "pingMs" : 0, "syncingTo" : "mongodb3.example.net:27017" }, { "_id" : 1, "name" : "mongodb2.example.net:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 644, "optime" : { "t" : 1368626414, "i" : 1 }, "optimeDate" : ISODate("2013-05-15T14:00:14Z"), "self" : true }, { "_id" : 2, "name" : "mongodb3.example.net:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 299, "optime" : { "t" : 1368626414, "i" : 1 }, "optimeDate" : ISODate("2013-05-15T14:00:14Z"), "lastHeartbeat" : ISODate("2013-05-15T06:16:02Z"), "lastHeartbeatRecv" : ISODate("1970-01-01T00:00:00Z"), "pingMs" : 3, "lastHeartbeatMessage" : "syncing to: mongodb2.example.net:27017", "syncingTo" : "mongodb2.example.net:27017" } ], "ok" : 1 }
可以看出,195自动成为Primary,196继续为Secondary,而193的health已经变为0,stateStr变为(not reachable/healthy)。
我们可以重新启动193上的mongod,并将其priority设为2,高于另外两个成员的优先级:
rs0: PRIMARY> conf = rs.config() rs0: PRIMARY> conf.members[0].priority=2 rs0: PRIMARY> rs.reconfig(conf)
查看当前副本集成员配置信息:
rs0: PRIMARY > rs.config() { "_id" : "rs0", "version" : 2, "members" : [ { "_id" : 0, "host" : "mongodb1.example.net:27017", "priority" : 2 }, { "_id" : 1, "host" : "mongodb2.example.net:27017" }, { "_id" : 2, "host" : "mongodb3.example.net:27017" } ] }
在之前的primary上,几秒钟后命令标识已经变为SECONDARY了,查看副本集状态:
rs0:SECONDARY> rs.status() { "set" : "rs0", "date" : ISODate("2013-05-15T06:18:42Z"), "myState" : 2, "syncingTo" : "mongodb1.example.net:27017", "members" : [ { "_id" : 0, "name" : "mongodb1.example.net:27017", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 41, "optime" : { "t" : 1368626414, "i" : 1 }, "optimeDate" : ISODate("2013-05-15T14:00:14Z"), "lastHeartbeat" : ISODate("2013-05-15T06:18:41Z"), "lastHeartbeatRecv" : ISODate("1970-01-01T00:00:00Z"), "pingMs" : 1, "syncingTo" : "mongodb2.example.net:27017" }, { "_id" : 1, "name" : "mongodb2.example.net:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 803, "optime" : { "t" : 1368626414, "i" : 1 }, "optimeDate" : ISODate("2013-05-15T14:00:14Z"), "errmsg" : "syncing to: mongodb1.example.net:27017", "self" : true }, { "_id" : 2, "name" : "mongodb3.example.net:27017", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 458, "optime" : { "t" : 1368626414, "i" : 1 }, "optimeDate" : ISODate("2013-05-15T14:00:14Z"), "lastHeartbeat" : ISODate("2013-05-15T06:18:41Z"), "lastHeartbeatRecv" : ISODate("2013-05-15T06:18:42Z"), "pingMs" : 0, "lastHeartbeatMessage" : "syncing to: mongodb1.example.net:27017", "syncingTo" : "mongodb1.example.net:27017" } ], "ok" : 1 }
发现193已经升级为Primary,因为其优先级高于其他两个。
尝试在SECONDARY上插入数据:
rs0:SECONDARY> db.kz.insert({"user1":111}) not master
返回not master。所以必须使用驱动连接到PRIMARY上进行操作,必须在应用上对这种故障切换做进一步控制,保证是对PRIMARY进行操作。
可以通过rs.slaveOk()命令使该SECONDARY可以进行读操作:
rs0:SECONDARY> db.kz.find() error: { "$err" : "not master and slaveOk=false", "code" : 13435 } rs0:SECONDARY> rs.slaveOk() rs0:SECONDARY> db.kz.find() { "_id" : ObjectId("51932c0df2aa04a85bc95d33"), "user" : 1111 }
五、 分片集群配置
由于项目需要,要部署一个副本集的分片集群,有四个服务器用来部署mongoDB,我们将数据分为三个副本集分片,并分别部署三个config server和三个mongos, 则具体分配如下:
Server 218.30.117.193: 10001 shard1 10002 shard2 10003 shard3 20001 config1 Server 218.30.117.195: 10001 shard1 10002 shard2 10003 shard3 20002 config2 Server 218.30.117.196: 10001 shard1 10002 shard2 10003 shard3 20003 config3 Server 218.30.117.197: 30001 mongos1 30002 mongos2 30003 mongos3
1. 启动Config server实例
配置数据库是存放集群元数据的mongod实例。使用--configsvr选项指定一个mongod为一个配置服务器。每个配置服务器保存集群一份全部的元数据。
在生产环境下,你必须部署三个配置服务实例,每个运行在不同的服务器上,以确保良好的运行时间和数据安全。在测试环境下,你能在一台单独的服务器上运行三个配置服务。
配置服务实例接收相对较小的流量,并且占用系统资源较少。因此,可以在运行这个实例的系统上同时运行其它实例。
(1) 为每个配置服务实例创建一个数据目录。默认的情况下,配置服务将数据文件存储在/data/configdb目录下。
(2) 运行三个配置服务实例。使用下面命令:
mongod --configsvr --dbpath <path> --port <port> --logpath <logpath>
如端口为20001,数据存放目录为/mongo-data/config1,日志路径为/mongo-data/config1/log.log,并创建服务进程,命令如下:
mongod --fork --configsvr --port 20001 --dbpath /mongo-data/config1/ --logpath /mongo-data/config1/log.log
2. 启动mongos实例
Mongos实例是轻量级的,并且不需要数据目录。你能在一个运行其它集群组件的系统上运行一个mongos实例,如一个应用服务或者一个mongod进程。Mongos的默认端口是27017。
当你启动一个mongos实例时,在配置文件中或命令行中指定那三个config server的主机名。为了操作灵活,使用配置服务器的DNS名而不是IP地址。如果你没有使用可解析的主机名,在没有重启每个mongos和mongod实例时不能改变配置服务器名或IP地址。
启动一个mongos实例,命令行如下:
mongos --configdb <config server hostnames>
举个例子,启动一个mongos连接以下配置服务器:
cfg0.example.net
cfg1.example.net
cfg2.example.net
将运行下面的命令:
mongos --configdb cfg0.example.net:27019 cfg1.example.net:27019 cfg2.example.net:27019
3. 向集群中添加分片
一个分片可以是一个单独的mongod或者是一个副本集。在生产环境中,每个分片应该是一个副本集。
(1) 从mongo shell连接到mongos实例上。执行下面的命令:
mongo --host <hostname of machine running mongos> --port <port mongos listens on>
举个例子:
mongo --host mongos0.example.net --port 27017
(2) 使用sh.addShard()命令将每个分片添加到集群中,如下面的例子所示。为每个分片单独的使用sh.addShard()命令。如果分片是一个副本集,则要指定副本集名并指定组的一个成员。
下面的例子使用了sh.addShard()命令添加一个分片:
- 通过一个运行在mongodb0.example.net上的27017端口的成员,将一个名为rs1的副本集分片添加到集群中,命令如下:
sh.addShard(“rs1/mongodb0.example.net:27017”)
版本2.0.3改变的。
在这个版本之前,必须指定所有的副本集成员,如:
sh.addShard( "rs1/mongodb0.example.net:27017,mongodb1.example.net:27017,mongodb2.example.net:27017" )
- 添加一个运行在mongodb0.example.net服务器上的27017端口的,命令如下:
sh.addShard( "mongodb0.example.net:27017" )
4. 开启一个数据库的分片功能
在对一个集合分片之前,你必须开启这个集合数据库的分片功能。开启这个功能并不会重新分配数据,但能使集合能够分片。
一旦你开启了一个数据库的分片功能,MongoDB会分配一个主片,使MongoDB在分片之前将所有的数据保存在这个数据库上。
- 在mongo shell中连接一个mongos实例,使用以下命令语法:
mongo --host <hostname of machine running mongos> --port <port mongos listens on>
- 执行sh.enableSharding()方法,指定想要开启分片功能的数据库的名字。使用下面的语法:
sh.enableSharding("<database>")
也可以使用enableSharding命令,语法如下:
db.runCommand( { enableSharding : <database> } )
- 开启一个collection的分片功能
你可以对每个collection开启分片功能。
(1) 决定使用什么作为片键。片键的选择会影响分片的性能。
(2) 如果一个collection已经包含数据,则必须在片键的字段上使用ensureIndex()命令建立一个索引。如果collection是空的,则MongoDB将在sh.shardCollection()阶段建立一个索引。
(3) 通过在mongo shell中执行sh.shardCollection()方法开启一个collection的分片功能。这个方法的使用语法如下:
db.shardCollection(“<database>.<collection>”, shard-key-pattern)
用你的数据库命名空间,由数据库名,一个点和collection的名字组成,替换<database>.<collection>字符串。Shard-key-pattern代表你的片键,你可以使用与一个索引相同的模式。
例子:
sh.shardCollection("records.people", { "zipcode": 1, "name": 1 } ) sh.shardCollection("people.addresses", { "state": 1, "_id": 1 } ) sh.shardCollection("assets.chairs", { "type": 1, "_id": 1 } ) db.alerts.ensureIndex( { _id : "hashed" } ) sh.shardCollection("events.alerts", { "_id": "hashed" } )
这些分片操作按顺序依次表示为:
- 数据库records 中的People集合使用了片键{ “zipcode” : 1, “name” : 1 }
这个片键通过字段zipcode的值进行数据分配。如果这个有大量相同的值,则会根据name字段的值对块进行分裂。
- 数据库people的addresses集合使用了片键{ “state” : 1, “_id” : 1 }
这个片键通过字段state的值进行数据分配。如果这个有大量相同的值,则会根据_id字段的值对块进行分裂。
- 数据库assets的chairs集合使用了片键{ “type” : 1, “_id” : 1 }
这个片键通过字段type的值进行数据分配。如果这个有大量相同的值,则会根据_id字段的值对块进行分裂。
- 数据库events的alerts集合使用了片键{ “_id” : “hashed” }
在版本2.4中最新出现的。
这个片键通过字段_id的散列值进行数据分配。MongoDB计算_id字段的散列值作为散列索引,它将提供集群中文档的均匀分布。
六、 分片部署
1. 启动mongod
Server 218.30.117.193: # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10001 --dbpath /usr/tmp/shard1/ --logpath /usr/tmp/shard1/log.log --replSet shard1 --rest # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10002 --dbpath /usr/tmp/shard2/ --logpath /usr/tmp/shard2/log.log --replSet shard2 --rest # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10003 --dbpath /usr/tmp/shard3/ --logpath /usr/tmp/shard3/log.log --replSet shard3 --rest Server 218.30.117.195: # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10001 --dbpath /usr/tmp/shard1/ --logpath /usr/tmp/shard1/log.log --replSet shard1 --rest # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10002 --dbpath /usr/tmp/shard2/ --logpath /usr/tmp/shard2/log.log --replSet shard2 --rest # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10003 --dbpath /usr/tmp/shard3/ --logpath /usr/tmp/shard3/log.log --replSet shard3 --rest Server 218.30.117.196: # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10001 --dbpath /usr/tmp/shard1/ --logpath /usr/tmp/shard1/log.log --replSet shard1 --rest # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10002 --dbpath /usr/tmp/shard2/ --logpath /usr/tmp/shard2/log.log --replSet shard2 --rest # /usr/local/mongodb/bin/mongod --fork --shardsvr --port 10003 --dbpath /usr/tmp/shard3/ --logpath /usr/tmp/shard3/log.log --replSet shard3 --rest
2. 启动config Server
Server 218.30.117.193: # /usr/local/mongodb/bin/mongod --fork --configsvr --port 20001 --dbpath /usr/tmp/config1/ --logpath /usr/tmp/config1/log.log --rest Server 218.30.117.195: # /usr/local/mongodb/bin/mongod --fork --configsvr --port 20002 --dbpath /usr/tmp/config1/ --logpath /usr/tmp/config1/log.log --rest Server 218.30.117.196: # /usr/local/mongodb/bin/mongod --fork --configsvr --port 20003 --dbpath /usr/tmp/config1/ --logpath /usr/tmp/config1/log.log --rest
3. 启动mongos
这里为了方便的看到分片过程,将chunkSize设置为1,也就是每个块大小为1MB。
218.30.117.197: # /usr/local/mongodb/bin/mongos --fork --port 30001 --logpath /usr/tmp/route1/log.log --chunkSize 1 --configdb 218.30.117.193:20001,218.30.117.195:20002,218.30.117.196:20003 # /usr/local/mongodb/bin/mongos --fork --port 30002 --logpath /usr/tmp/route2/log.log --chunkSize 1 --configdb 218.30.117.193:20001,218.30.117.195:20002,218.30.117.196:20003 # /usr/local/mongodb/bin/mongos --fork --port 30003 --logpath /usr/tmp/route3/log.log --chunkSize 1 --configdb 218.30.117.193:20001,218.30.117.195:20002,218.30.117.196:20003
4. 在mongod上配置副本集
先连接到某个副本集的成员上,然后初始化副本集配置:
连接到218.30.117.193:10001上初始化副本集shard1:
# /usr/local/mongodb/bin/mongo 218.30.117.193:10001/admin MongoDB shell version: 2.4.2 connecting to: 218.30.117.193:10001/admin > config={_id:'shard1', members:[{_id:0, host:'218.30.117.193:10001'}, {_id:1, host:'218.30.117.195:10001'}, {_id:2, host:'218.30.117.196:10001'}]} > rs.initiate(config)
连接到218.30.117.195:10002上初始化副本集shard2:
# /usr/local/mongodb/bin/mongo 218.30.117.195:10002/admin MongoDB shell version: 2.4.2 connecting to: 218.30.117.195:10002/admin > config={_id:'shard2', members:[{_id:0, host:'218.30.117.193:10002'}, {_id:1, host:'218.30.117.195:10002'}, {_id:2, host:'218.30.117.196:10002'}]} > rs.initiate(config)
连接到218.30.117.196:10003上初始化副本集shard3:
# /usr/local/mongodb/bin/mongo 218.30.117.196:10003/admin MongoDB shell version: 2.4.2 connecting to: 218.30.117.196:10003/admin > config={_id:'shard3', members:[{_id:0, host:'218.30.117.193:10003'}, {_id:1, host:'218.30.117.195:10003'}, {_id:2, host:'218.30.117.196:10003'}]} > rs.initiate(config)
可以通过rs.status()命令查看副本集配置结果:
> rs.status() { "set" : "shard3", "date" : ISODate("2013-05-14T17:44:05Z"), "myState" : 1, "members" : [ { "_id" : 0, "name" : "218.30.117.193:10003", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 147, "optime" : { "t" : 1368553298, "i" : 1 }, "optimeDate" : ISODate("2013-05-14T17:41:38Z"), "lastHeartbeat" : ISODate("2013-05-14T17:44:03Z"), "lastHeartbeatRecv" : ISODate("2013-05-14T17:44:05Z"), "pingMs" : 0, "syncingTo" : "218.30.117.196:10003" }, { "_id" : 1, "name" : "218.30.117.195:10003", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 147, "optime" : { "t" : 1368553298, "i" : 1 }, "optimeDate" : ISODate("2013-05-14T17:41:38Z"), "lastHeartbeat" : ISODate("2013-05-14T17:44:03Z"), "lastHeartbeatRecv" : ISODate("2013-05-14T17:44:03Z"), "pingMs" : 1, "syncingTo" : "218.30.117.196:10003" }, { "_id" : 2, "name" : "218.30.117.196:10003", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1109, "optime" : { "t" : 1368553298, "i" : 1 }, "optimeDate" : ISODate("2013-05-14T17:41:38Z"), "self" : true } ], "ok" : 1 }
5. 向集群添加副本集分片
先通过mongo shell连接到mongos实例:
# /usr/local/mongodb/bin/mongo 218.30.117.197:30001/admin MongoDB shell version: 2.4.2 connecting to: 218.30.117.197:30001/admin mongos> db.runCommand({addshard:"shard1/218.30.117.193:10001", name:"ShardSet1"}) { "shardAdded" : "ShardSet1", "ok" : 1 } mongos> db.runCommand({addshard:"shard2/218.30.117.195:10002", name:"ShardSet2"}) { "shardAdded" : "ShardSet2", "ok" : 1 } mongos> db.runCommand({addshard:"shard3/218.30.117.195:10003", name:"ShardSet3"}) { "shardAdded" : "ShardSet3", "ok" : 1 }
通过db.runCommand({listshards: 1})查看分片集群的信息:
mongos> db.runCommand({listshards: 1}) { "shards" : [ { "_id" : "ShardSet1", "host" : "shard1/218.30.117.193:10001,218.30.117.195:10001,218.30.117.196:10001" }, { "_id" : "ShardSet2", "host" : "shard2/218.30.117.193:10002,218.30.117.195:10002,218.30.117.196:10002" }, { "_id" : "ShardSet3", "host" : "shard3/218.30.117.193:10003,218.30.117.195:10003,218.30.117.196:10003" } ], "ok" : 1 }
6. 开启数据库的分片功能
我们的数据库名为page_db,则命令如下
mongos> sh.enableSharding("page_db") { "ok" : 1 }
7. 开启数据库中users集合的分片功能,并指定_id的散列值组作为片键
mongos> sh.shardCollection("page_db.users", {_id : "hashed"}) { "collectionsharded" : "page_db.users", "ok" : 1 }
七、 总结
副本集大量应用在生产环节下,可以给MongoDB提供冗余,虽然能够自动故障切换,但从应用角度上来说还是需要额外的逻辑的。
分片功能在少数据量时没有必要,只是增加了架构的复杂性。但如果数据量大,访问量大的情况下,分片还是很有必要的。