MongoDB之旅(下)
介绍副本集、分片、备份等内容
本文所使用的MongoDB版本为3.4.2
副本集
建立
在本地启动所有成员,分别设置不同的路径和端口,使用相同的标识符student
。
# terminal-1
mongod --dbpath ~/Desktop/db --port 27017 --replSet "student"
# terminal-2
mongod --dbpath ~/Desktop/db-rs0 --port 27016 --replSet "student"
# terminal-3
mongod --dbpath ~/Desktop/db-rs1 --port 27015 --replSet "student"
配置
连接一个节点
mongo --port 27017
创建配置对象与初始化
> var config = {
"_id" : "student",
"members" : [
{"_id": 0, "host": "127.0.0.1:27017"},
{"_id": 1, "host": "127.0.0.1:27016"},
{"_id": 2, "host": "127.0.0.1:27015"}
]
}
> rs.initiate(config)
所连接的节点解析这个对象后会将配置文件发给其他成员,所有成员配置完成后会自动选出一个主节点(primary member),其他成员为备份节点(secondary member)。
这里27017端口的副本集被选为主节点,其他两个为备份节点,先在主节点中添加一条数据。
student:PRIMARY> db.stu.insert({"name":"yrq","grade":"3","class":"7"})
默认情况下备份节点是不可读的,需要执行db.setSlaveOk()
后才可读取数据。
> mongo --port 27016
student:SECONDARY> db.stu.find()
Error: error: {
"ok" : 0,
"errmsg" : "not master and slaveOk=false",
"code" : 13435,
"codeName" : "NotMasterNoSlaveOk"
}
student:SECONDARY> db.setSlaveOk()
student:SECONDARY> db.stu.find()
{ "_id" : ObjectId("58f58ded93e9bbcb0bceb3c5"), "name" : "yrq", "grade" : "3", "class" : "7" }
查看配置与状态
-
rs.conf()
student:PRIMARY> rs.conf() { "_id" : "student", "version" : 1, # 每个修改配置时这个值都会自增+1 "protocolVersion" : NumberLong(1), "members" : [ # 副本集成员列表 { "_id" : 0, "host" : "127.0.0.1:27017", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 1, "host" : "127.0.0.1:27016", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 }, { "_id" : 2, "host" : "127.0.0.1:27015", "arbiterOnly" : false, "buildIndexes" : true, "hidden" : false, "priority" : 1, "tags" : { }, "slaveDelay" : NumberLong(0), "votes" : 1 } ], "settings" : { "chainingAllowed" : true, "heartbeatIntervalMillis" : 2000, "heartbeatTimeoutSecs" : 10, "electionTimeoutMillis" : 10000, "catchUpTimeoutMillis" : 2000, "getLastErrorModes" : { }, "getLastErrorDefaults" : { "w" : 1, "wtimeout" : 0 }, "replicaSetId" : ObjectId("58f58a448ab72530955abf84") } }
-
db.isMaster()
查看当前mongod实例的角色,若mongod实例是副本集中的成员,则通过
ismaster
与secondary
可以判断它是主节点还是备份节点。student:PRIMARY> db.isMaster() { "hosts" : [ "127.0.0.1:27017", "127.0.0.1:27016", "127.0.0.1:27015" ], "setName" : "student", "setVersion" : 1, "ismaster" : true, # 是否为主节点 "secondary" : false, # 是否为备份节点 "primary" : "127.0.0.1:27017", # 主节点成员 "me" : "127.0.0.1:27017", # 自己 ... }
-
rs.status()
显示当前副本集的状态。
student:PRIMARY> rs.status() { "set" : "student", "date" : ISODate("2017-04-18T04:03:52.781Z"), "myState" : 1, "term" : NumberLong(1), "heartbeatIntervalMillis" : NumberLong(2000), ... "members" : [ { "_id" : 0, "name" : "127.0.0.1:27017", "health" : 1, "state" : 1, # 1表示主节点,可进行写操作,有投票资格 "stateStr" : "PRIMARY", ... "self" : true }, { "_id" : 1, "name" : "127.0.0.1:27016", "health" : 1, "state" : 2, # 2表示备份节点,有投票资格 "stateStr" : "SECONDARY", ... }, { "_id" : 2, "name" : "127.0.0.1:27015", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", ... } ], "ok" : 1 }
修改配置
-
添加成员
启动一个成员
mongod --dbpath ~/Desktop/db-rs2 --port 27014 --replSet "student"
添加到副本集中
student:PRIMARY> rs.add("127.0.0.1:27014") { "ok" : 1 } student:PRIMARY> rs.status().members.length 4
-
删除成员
student:PRIMARY> rs.remove("127.0.0.1:27014") { "ok" : 1 } student:PRIMARY> rs.status().members.length 3
-
将主节点降级为备份节点
student:PRIMARY> rs.stepDown()
-
阻止选举
若主节点要进行维护,不需要再这期间选举新的主节点,可以在每个备份节点执行freeze命令冻结状态,始终为备份节点。
student:SECONDARY> rs.freeze(1000000)
输入参数为秒数,需要释放时执行rs.freeze(0)即可。
设计
仲裁成员
仲裁成员(arbiter)不保存数据,不为客户端提供服务,仅参与投票选举,避免出现平票的情况,可部署在性能较差的服务器上。 添加仲裁成员的方法:
student:PRIMARY> rs.addArb("127.0.0.1:27014")
或
student:PRIMARY> rs.add({"_id":3,"host":"127.0.0.1:27014","arbiterOnly":true})
隐藏成员
隐藏成员对用户不可见,不为客户端提供服务。只有优先级为0的成员才能被隐藏,
隐藏一个成员
student:PRIMARY> var config = rs.conf()
student:PRIMARY> config.members[2].hidden = true;
true
student:PRIMARY> config.members[2].priority = 0;
0
student:PRIMARY> rs.reconfig(config)
隐藏成员对isMaster()是不可见的,对status()和conf()是可见的。
student:PRIMARY> rs.status().members.length
3
student:PRIMARY> rs.conf().members.length
3
student:PRIMARY> db.isMaster().hosts.length
2
取消隐藏需要将hidden属性设为false或者删掉hidden。
延迟备份
config中的每个成员都有一个slaveDelay选项,用来设置备份节点同步主节点的延迟,默认为0,表示当主节点有操作改变时立即同步。
设置延迟可以防止对主节点的误操作,如删除或有严重bug的情况。
设置这个选项要求成员的优先级为0。
创建索引
有时备份节点并不需要像主节点拥有索引,可以通过设置"buildIndexes" : false
来阻止备份节点创建索引,这个修改是永久操作,无法再次恢复为可拥有索引的状态。
设置这个选项要求成员的优先级为0。
同步
复制功能是使用操作日志oplog实现的,操作日志包含了主节点每一次写操作,备份节点查询oplog获取需要执行的操作,然后在自己的数据集上执行操作。
心跳
每个成员每隔一段时间都会向其它成员发送心跳请求(heartbeat request),用于检查每个成员的状态。
成员状态
序号 | 名称 | 描述 |
---|---|---|
0 | STARTUP | 成员刚启动的状态,若继续加载配置则进入STARTUP2状态 |
1 | PRIMARY | 主节点,有投票资格 |
2 | SECONDARY | 备份节点,有投票资格 |
3 | RECOVERING | 成员在进行启动自检,或刚完成回滚或重同步操作的过渡,有投票资格 |
5 | STARTUP2 | 已启动在初始化过程中 |
6 | UNKNOWN | 状态对其它成员是未知的 |
7 | ARBITER | 仲裁成员,不复制数据,仅参与选举 |
8 | DOWN | 其它成员不可达 |
9 | ROLLBACK | 成员正在执行回滚操作,不能进行读操作 |
10 | REMOVED | 成员被移出副本集 |
分片
作用
- 增加可用RAM
- 增加可用磁盘空间
- 减轻单台服务器的负载
- 处理单个mongod无法承受的吞吐量
分片集群
每个分片集群都由三个部分组成:
- 分片(shard) - 每个分片包含分片数据中的一个子集,分片可以是副本集。
- mongos - mongos作为查询数据的路由,提供客户端与分片集群间的接口。
- 配置服务器(config server) - 保存集群的元数据与配置,在3.4版本后要求配置服务器必须为副本集。
生产环境中的配置:
- 3个及以上的配置服务器
- 3个及以上的分片
- 1个及以上的mongos路由
开发环境中的配置:
- 一个配置服务器(副本集)
- 至少一个分片(副本集)
- 一个mongo实例
启动
端口设计:
- 27014~27016 配置服务器
- 27001~27003 分片1(副本集)
- 27004 分片2
- 27000 mongos
-
创建配置服务器副本集
mongos需要从配置服务器中获取配置信息,因此需要优先启动.
# terminal-1 mongod --configsvr --dbpath ~/Desktop/db-conf1 --port 27016 --replSet "config" # terminal-2 mongod --configsvr --dbpath ~/Desktop/db-conf2 --port 27015 --replSet "config" # terminal-3 mongod --configsvr --dbpath ~/Desktop/db-conf3 --port 27014 --replSet "config"
配置服务器不需要分配太大的空间,它存放数据分布表.
初始化副本集:
> rs.initiate( ... { ... _id: "config", ... configsvr: true, ... members: [ ... { _id : 0, host : "127.0.0.1:27016" }, ... { _id : 1, host : "127.0.0.1:27015" }, ... { _id : 2, host : "127.0.0.1:27014" } ... ] ... })
-
创建分片 创建副本集分片
# terminal-4 mongod --shardsvr --dbpath ~/Desktop/db-sh1 --port 27001 --replSet "shard" # terminal-5 mongod --shardsvr --dbpath ~/Desktop/db-sh2 --port 27002 --replSet "shard" # terminal-6 mongod --shardsvr --dbpath ~/Desktop/db-sh3 --port 27003 --replSet "shard"
配置副本集
> rs.initiate( ... { ... _id: "shard", ... members: [ ... { _id : 0, host : "127.0.0.1:27001" }, ... { _id : 1, host : "127.0.0.1:27002" }, ... { _id : 2, host : "127.0.0.1:27003" } ... ] ... })
创建普通分片
# terminal-7 mongod --shardsvr --dbpath ~/Desktop/db-sh4 --port 27004
-
启动mongos
mongos --configdb config/127.0.0.1:27016,127.0.0.1:27015,127.0.0.1:27014
config
为配置服务器副本集名称,斜杠后为具体数据集地址。连接到mongos
mongo --port 27000
-
添加分片
mongos> sh.addShard("shard/127.0.0.1:27001,127.0.0.1:27002,127.0.0.1:27003") mongos> sh.addShard("127.0.0.1:27004")
shard
为分片副本集名称,斜杠后为具体数据集地址,若为普通数据集则不用添加名称。 -
查看状态
mongos> sh.status().shards --- Sharding Status --- ... shards: { "_id" : "shard", "host" : "shard/127.0.0.1:27001,127.0.0.1:27002,127.0.0.1:27003", "state" : 1 } { "_id" : "shard0001", "host" : "127.0.0.1:27004", "state" : 1 } ... databases: { "_id" : "foo", "primary" : "shard", "partitioned" : false }
可以看到有两个分片,一个是副本集"shard",一个是普通分片"shard0001"。 databases中的_id为数据库名。primary表示主分片,为随机选择的,所有分片都会在主分片上。partitioned表示是否启用了分片。
先在mongos中操作添加一些数据
mongos> for(var i=0;i<5000;i++){ ... db.user.insert( ... { ... "name":"user_"+i, ... "created": new Date() ... }); ... }
-
启用分片
mongos> use foo switched to db foo mongos> sh.enableSharding("foo")
-
创建索引
对集合分片时需要选择一个片键,为集合的一个键,分片时会根据这个片键拆分数据。只有具有索引的键才能成为片键,因此先要在想使用片键的键上创建索引。
mongos> db.user.ensureIndex({"name":1})
-
对集合进行分片
mongos> sh.shardCollection("foo.user",{"name":1})
-
分片后的状态
mongos> sh.status().shards --- Sharding Status --- ... foo.user shard key: { "name" : 1 } unique: false balancing: true chunks: shard 1 { "name" : { "$minKey" : 1 } } -->> { "name" : { "$maxKey" : 1 } } on : shard Timestamp(1, 0)
可以看到分片后只有一个块,存储在分片"shard"上,"$minKey"与"$maxKey"表示键所对应值范围的端值,
-
查看块(chunk)的状态
需要在config db下操作
mongos> use config switched to db config mongos> db.chunks.find().pretty() { "_id" : "foo.user-name_MinKey", "ns" : "foo.user", "min" : { "name" : { "$minKey" : 1 } }, "max" : { "name" : { "$maxKey" : 1 } }, "shard" : "shard", "lastmod" : Timestamp(1, 0), "lastmodEpoch" : ObjectId("58f75c5b9cde39278500058c") }
片键
块
备份
文件系统快照
使用文件系统快照有两个条件:
- 需要文件系统本身支持快照(如Linux的LVM和Amazon EBS的EC2)。
- 启用journaling日记系统。
cp或rsync
若系统不支持快照,可直接使用cp、rsync或其它工具备份数据。
-
锁定数据库,禁止写入等操作
> db.fsync()
-
复制数据库文件到指定目录
$ cp -R ~/Desktop/db/* ~/Desktop/backup
-
复制完成后解锁数据库
> db.fsyncUnlock()
mongodump
mongodump和mongorestore是备份小型mongo数据库的有效工具,不适合大型的系统的备份。
-
使用mongodump创建备份
$ mongodump 2017-04-10T23:20:46.221+0800 writing admin.system.version to 2017-04-10T23:20:46.222+0800 done dumping admin.system.version (1 document) 2017-04-10T23:20:46.223+0800 writing test.users to 2017-04-10T23:20:46.223+0800 writing test.comments to 2017-04-10T23:20:46.224+0800 done dumping test.comments (40 documents) 2017-04-10T23:20:46.228+0800 done dumping test.users (1124 documents)
可以设置–dbpath指定数据文件进行备份,若mongod正在运行则不要设置使用这个选项,
-
使用mongorestore恢复mongodump产生的备份
$ mongorestore 2017-04-10T23:22:26.789+0800 using default 'dump' directory 2017-04-10T23:22:26.789+0800 preparing collections to restore from 2017-04-10T23:22:26.790+0800 reading metadata for test.comments from dump/test/comments.metadata.json 2017-04-10T23:22:26.791+0800 reading metadata for test.users from dump/test/users.metadata.json 2017-04-10T23:22:26.791+0800 restoring test.comments from dump/test/comments.bson 2017-04-10T23:22:26.792+0800 restoring test.users from dump/test/users.bson ... 2017-04-10T23:22:26.842+0800 restoring indexes for collection test.users from metadata 2017-04-10T23:22:26.843+0800 finished restoring test.users (1124 documents) 2017-04-10T23:22:26.843+0800 done
参考
- 原文作者:yrq110
- 原文链接:http://yrq110.me/post/database/mongo-travel-part-2/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。