复制的重要性不再多说,其主要就是提供数据保护,数据高可用和灾难恢复。

复制是跨多个mongodb服务器分布和维护的方法。mongodb可以把数据从一个节点复制到其他节点并在修改时进行同步。

mongodb的复制有两种方式: 副本集复制和主从架构复制。这两种方法类似,主节点接收所有的写请求,然后所有的从节点读取,并且异步同步所有的数据。

主从复制和副本集使用了相同的复制机制,但是副本集额外增加了自动化灾备机制,如果主节点宕机,无论什么原因,其中一个从节点会自动提升为主节点。除此之外副本集还提供了其他改进,比如更易于恢复和更复杂地部署网络拓扑。线上环境几乎全都是使用的副本集,因此这里只会有关于副本集的介绍。

下面会实际搭建一个最基本的副本集,然后会说明副本集的原理?

【MongoDB的启动必须从配置文件启动,不要用命令行】

第一步:配置文件中指定副本集的名称

#在三台服务器的配置文件中均要有如下设置
replication:
oplogSizeMB: #指定oplog的日志大小,注意这里大小只是为了测试
replSetName: lianxi #指定副本集的名称,在一个副本集中名称要保持一致

第二步:配置文件启动MongoDB服务器:

[root@test1 ~]# cd /usr/local/mongodb/bin
[root@test1 bin]# ./mongod -f ../conf/mongod.conf
about to fork child process, waiting until server is ready for connections.
forked process:
child process started successfully, parent exiting
[root@test1 bin]#

第三步:配置副本集

#选中其中一台MongoDB服务器作为primary.
[root@test2 bin]# ./mongo #进入shell交互界面
MongoDB shell version v3.4.2
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.
Server has startup warnings:
--06T13::43.676+ I STORAGE [initandlisten]
--06T13::43.676+ I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
--06T13::43.676+ I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem
--06T13::43.809+ I CONTROL [initandlisten]
--06T13::43.809+ I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
--06T13::43.809+ I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
--06T13::43.809+ I CONTROL [initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.
--06T13::43.809+ I CONTROL [initandlisten]
> rs.initiate() #初始化操作
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "test2:27017",
"ok" :
}
lianxi:SECONDARY> #初始化成功之后,提示会变为SECONDARY
lianxi:PRIMARY> #敲下回车,会变为PRIMARY
#初始化成功之后,添加副本集成员
lianxi:PRIMARY> rs.add("10.0.102.214:27017") #添加从节点
{ "ok" : } #添加仲裁节点
lianxi:PRIMARY> rs.addArb("10.0.102.220:27017")
{ "ok" : }

若上面的步骤没有报错,说明副本集搭建成功。

说一个这里搭建遇见的一个问题:

> rs.initiate()
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "test3:27017",
"ok" : 1
} #初始化成功
test_repl:SECONDARY> rs.add("10.0.102.181:27017") #添加节点成功【但是日志会在等一会之后报错】
{ "ok" : 1 }
test_repl:PRIMARY> rs.addArb("10.0.102.204:27017") #若不查看日志直接添加仲裁节点,会报如下错误!
2018-11-05T10:33:12.753+0800 E QUERY [thread1] Error: error doing query: failed: network error while attempting to run command 'count' on host '127.0.0.1:27017' :
DB.prototype.runCommand@src/mongo/shell/db.js:132:1
DB.prototype.runReadCommand@src/mongo/shell/db.js:109:16
DBQuery.prototype.count@src/mongo/shell/query.js:380:15
DBCollection.prototype.count@src/mongo/shell/collection.js:1700:12
rs.add@src/mongo/shell/utils.js:1213:1
rs.addArb@src/mongo/shell/utils.js:1253:12
@(shell):1:1
2018-11-05T10:33:12.756+0800 I NETWORK [thread1] trying reconnect to 127.0.0.1:27017 (127.0.0.1) failed
2018-11-05T10:33:12.757+0800 I NETWORK [thread1] reconnect 127.0.0.1:27017 (127.0.0.1) ok test_repl:SECONDARY> #这里的提示变为SECONDARY 这个问题查了好久,后来在日志文件中发现是第一步添加节点成功,日志就报错了意识是主节点连接不上从节点,但是在命令行指定host参数是可以连接的,于是就各种纠结!
最后无意中看了/etc/hosts文件,发现是hosts文件的域名解析指向错误。好吧,就是这里的问题!但是费了好长时间!

副本集搭建遇见的一个问题

这个副本集中含有一个主节点用来写入数据,一个从节点,和一个仲裁节点,整个结构如下:

这是MongoDB副本集的最小的节点数量,一旦主宕机,根据选举原则(一个服务器一票)从服务器会被提升为主服务器继续提供服务。MongoDB副本集为了防止出现脑裂的情况,副本集的个数应该为奇数。

测试副本集:

查看当前副本集的状态,可以使用如下命令:

rs.status()

members数组中有三个成员,分别表示这主节点,从节点,和仲裁节点。

#在主上面写入数据
lianxi:PRIMARY> use mydb
switched to db mydb
lianxi:PRIMARY> db.cityinfo.insert({name: "HongKong", country: "CHINA"})
WriteResult({ "nInserted" : })
lianxi:PRIMARY> #从上面查看数据
lianxi:SECONDARY> show dbs; #从上面需要执行slaveOK命令才能查看数据
--06T14::47.696+ E QUERY [thread1] Error: listDatabases failed:{
"ok" : ,
"errmsg" : "not master and slaveOk=false",
"code" : ,
"codeName" : "NotMasterNoSlaveOk"
} :
_getErrorWithCode@src/mongo/shell/utils.js::
Mongo.prototype.getDBs@src/mongo/shell/mongo.js::
shellHelper.show@src/mongo/shell/utils.js::
shellHelper@src/mongo/shell/utils.js::
@(shellhelp2)::
lianxi:SECONDARY> rs.slaveOk() #执行命令后
lianxi:SECONDARY> show dbs;
admin .000GB
local .000GB
mydb .000GB
lianxi:SECONDARY> use mydb
switched to db mydb
lianxi:SECONDARY> show collections;
cityinfo
lianxi:SECONDARY> db.cityinfo.find().pretty() #数据已经同步到从上
{
"_id" : ObjectId("5be12eb19efff2d99afed619"),
"name" : "HongKong",
"country" : "CHINA"
}
lianxi:SECONDARY>

副本集测试

查看主的状态如下:

lianxi:PRIMARY> db.isMaster()
{
"hosts" : [
"test2:27017",
"10.0.102.214:27017"
],
"arbiters" : [
"10.0.102.220:27017"
],
"setName" : "lianxi",
"setVersion" : ,
"ismaster" : true,
"secondary" : false,
"primary" : "test2:27017",
"me" : "test2:27017",
"electionId" : ObjectId("7fffffff0000000000000001"),
"lastWrite" : {
"opTime" : {
"ts" : Timestamp(, ),
"t" : NumberLong()
},
"lastWriteDate" : ISODate("2018-11-06T06:07:21Z")
},
"maxBsonObjectSize" : ,
"maxMessageSizeBytes" : ,
"maxWriteBatchSize" : ,
"localTime" : ISODate("2018-11-06T06:07:22.355Z"),
"maxWireVersion" : ,
"minWireVersion" : ,
"readOnly" : false,
"ok" :
}

模仿故障转移,我们kill掉主进程,然后观察从服务器是否会变为主服务器。

#kill掉主服务器的进程
[root@test2 conf]# kill `netstat -lntp |grep mongod | awk '{print $NF}' | cut -d "/" -f1`
[root@test2 conf]# netstat -lntp | grep 27017 #查看从服务器
lianxi:SECONDARY> #敲回车会变为SECONDARY
lianxi:PRIMARY> db.isMaster()
{
    "hosts" : [
        "test2:27017",
        "10.0.102.214:27017"
    ],
    "arbiters" : [
        "10.0.102.220:27017"
    ],
    "setName" : "lianxi",
    "setVersion" : 3,
    "ismaster" : true,
    "secondary" : false,
    "primary" : "10.0.102.214:27017",
    "me" : "10.0.102.214:27017",
    "electionId" : ObjectId("7fffffff0000000000000002"),
    "lastWrite" : {
        "opTime" : {
            "ts" : Timestamp(1541485059, 1),
            "t" : NumberLong(2)
        },
        "lastWriteDate" : ISODate("2018-11-06T06:17:39Z")
    },
    "maxBsonObjectSize" : 16777216,
    "maxMessageSizeBytes" : 48000000,
    "maxWriteBatchSize" : 1000,
    "localTime" : ISODate("2018-11-06T06:17:49.181Z"),
    "maxWireVersion" : 5,
    "minWireVersion" : 0,
    "readOnly" : false,
    "ok" : 1
} #查看仲裁节点的日志 2018-11-06T14:15:28.293+0800 I -        [conn2] end connection 10.0.102.204:18284 (2 connections now open) #这个时间点kill掉了主服务器
2018-11-06T14:15:29.515+0800 I ASIO     [ReplicationExecutor] dropping unhealthy pooled connection to test2:27017
2018-11-06T14:15:29.515+0800 I ASIO     [ReplicationExecutor] after drop, pool was empty, going to spawn some connections
2018-11-06T14:15:29.515+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Connecting to test2:27017
2018-11-06T14:15:29.515+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Failed to connect to test2:27017 - HostUnreachable: Connection refused
2018-11-06T14:15:29.515+0800 I REPL     [ReplicationExecutor] Error in heartbeat request to test2:27017; HostUnreachable: Connection refused
2018-11-06T14:15:29.516+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Connecting to test2:27017
2018-11-06T14:15:29.516+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Failed to connect to test2:27017 - HostUnreachable: Connection refused
2018-11-06T14:15:29.516+0800 I REPL     [ReplicationExecutor] Error in heartbeat request to test2:27017; HostUnreachable: Connection refused
2018-11-06T14:15:29.516+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Connecting to test2:27017
2018-11-06T14:15:29.516+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Failed to connect to test2:27017 - HostUnreachable: Connection refused
2018-11-06T14:15:29.516+0800 I REPL     [ReplicationExecutor] Error in heartbeat request to test2:27017; HostUnreachable: Connection refused
2018-11-06T14:15:34.517+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Connecting to test2:27017
2018-11-06T14:15:34.517+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Failed to connect to test2:27017 - HostUnreachable: Connection refused
2018-11-06T14:15:34.518+0800 I REPL     [ReplicationExecutor] Error in heartbeat request to test2:27017; HostUnreachable: Connection refused
2018-11-06T14:15:34.518+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Connecting to test2:27017
2018-11-06T14:15:34.518+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Failed to connect to test2:27017 - HostUnreachable: Connection refused
2018-11-06T14:15:34.518+0800 I REPL     [ReplicationExecutor] Error in heartbeat request to test2:27017; HostUnreachable: Connection refused
2018-11-06T14:15:34.519+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Connecting to test2:27017
2018-11-06T14:15:34.519+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Failed to connect to test2:27017 - HostUnreachable: Connection refused
2018-11-06T14:15:34.519+0800 I REPL     [ReplicationExecutor] Error in heartbeat request to test2:27017; HostUnreachable: Connection refused
#终止连接10S之后,会切换从未主
2018-11-06T14:15:39.008+0800 I NETWORK  [thread1] connection accepted from 10.0.102.214:41150 #5 (2 connections now open)
2018-11-06T14:15:39.008+0800 I NETWORK  [conn5] received client metadata from 10.0.102.214:41150 conn5: { driver: { name: "NetworkInterfaceASIO-Replication", version: "3.4.2" }, os: { type: "Linux", name: "CentOS release 6.6 (Final)", architecture: "x86_64", version: "Kernel 2.6.32-504.el6.x86_64" } }
2018-11-06T14:15:39.420+0800 I REPL     [ReplicationExecutor] Member 10.0.102.214:27017 is now in state PRIMARY #切换之后仲裁节点还是会去尝试连接原来的主节点。
2018-11-06T14:15:39.519+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Connecting to test2:27017
2018-11-06T14:15:39.520+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Failed to connect to test2:27017 - HostUnreachable: Connection refused
2018-11-06T14:15:39.520+0800 I REPL     [ReplicationExecutor] Error in heartbeat request to test2:27017; HostUnreachable: Connection refused
2018-11-06T14:15:39.520+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Connecting to test2:27017
2018-11-06T14:15:39.521+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Failed to connect to test2:27017 - HostUnreachable: Connection refused
2018-11-06T14:15:39.521+0800 I REPL     [ReplicationExecutor] Error in heartbeat request to test2:27017; HostUnreachable: Connection refused
2018-11-06T14:15:39.521+0800 I ASIO     [NetworkInterfaceASIO-Replication-0] Connecting to test2:27017 #这是副本集已经变为一个主节点,一个仲裁节点,查看副本集的状态。
lianxi:PRIMARY> rs.status()
{
    "set" : "lianxi",
    "date" : ISODate("2018-11-06T06:25:06.254Z"),
    "myState" : 1,
    "term" : NumberLong(2),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1541484741, 1),
            "t" : NumberLong(1)
        },
        "appliedOpTime" : {
            "ts" : Timestamp(1541485499, 1),
            "t" : NumberLong(2)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1541485499, 1),
            "t" : NumberLong(2)
        }
    },
    "members" : [
        {
            "_id" : 0,
            "name" : "test2:27017",
            "health" : 0, #状态为0,处于不健康的状态
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",
            "uptime" : 0,
            "optime" : {
                "ts" : Timestamp(0, 0),
                "t" : NumberLong(-1)
            },
            "optimeDurable" : {
                "ts" : Timestamp(0, 0),
                "t" : NumberLong(-1)
            },
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2018-11-06T06:25:06.134Z"),
            "lastHeartbeatRecv" : ISODate("2018-11-06T06:15:27.891Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "Connection refused",
            "configVersion" : -1
        },
        {
            "_id" : 1,
            "name" : "10.0.102.214:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 3668,
            "optime" : {
                "ts" : Timestamp(1541485499, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2018-11-06T06:24:59Z"),
            "electionTime" : Timestamp(1541484939, 1),
            "electionDate" : ISODate("2018-11-06T06:15:39Z"),
            "configVersion" : 3,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "10.0.102.220:27017",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 3631,
            "lastHeartbeat" : ISODate("2018-11-06T06:25:05.689Z"),
            "lastHeartbeatRecv" : ISODate("2018-11-06T06:25:04.998Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 3
        }
    ],
    "ok" : 1
}

我们在新的主节点写入数据,然后恢复原来的主节点,查看副本集会怎样变化!

#在新的副本集写入数据
lianxi:PRIMARY> use mydb;
switched to db mydb
lianxi:PRIMARY> db.cityinfo.insert({name: "ShangHai", country: "CHINA"})
WriteResult({ "nInserted" : })
lianxi:PRIMARY> db.cityinfo.find().pretty()
{
"_id" : ObjectId("5be12eb19efff2d99afed619"),
"name" : "HongKong",
"country" : "CHINA"
}
{
"_id" : ObjectId("5be13451190311a6e9fa421e"),
"name" : "ShangHai",
"country" : "CHINA"
} #然后启动原来的主服务
[root@test2 bin]# ./mongod -f ../conf/mongod.conf #启动
about to fork child process, waiting until server is ready for connections.
forked process: 10395
child process started successfully, parent exiting
[root@test2 bin]# ./mongo
MongoDB shell version v3.4.2
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.4.2
lianxi:SECONDARY> use mydb;
switched to db mydb
lianxi:SECONDARY> show tables;
2018-11-06T14:28:05.575+0800 E QUERY    [thread1] Error: listCollections failed: {
    "ok" : 0,
    "errmsg" : "not master and slaveOk=false",
    "code" : 13435,
    "codeName" : "NotMasterNoSlaveOk"
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype._getCollectionInfosCommand@src/mongo/shell/db.js:805:1
DB.prototype.getCollectionInfos@src/mongo/shell/db.js:817:19
DB.prototype.getCollectionNames@src/mongo/shell/db.js:828:16
shellHelper.show@src/mongo/shell/utils.js:748:9
shellHelper@src/mongo/shell/utils.js:645:15
@(shellhelp2):1:1
lianxi:SECONDARY> rs.slaveOk()
lianxi:SECONDARY> db.cityinfo.find().pretty()
{
    "_id" : ObjectId("5be12eb19efff2d99afed619"),
    "name" : "HongKong",
    "country" : "CHINA"
}
{
    "_id" : ObjectId("5be13451190311a6e9fa421e"), #写入的数据已经同步过来了!
    "name" : "ShangHai",
    "country" : "CHINA"
}
lianxi:SECONDARY>

若我们想在原来的主恢复正常之后,副本集中自动的从主(原来的从)切换回来。这个时候可以设置每个副本集的权重,只要设置原来的主的权重大于原来从的权重即可!

lianxi:PRIMARY> cfg = rs.config()
lianxi:PRIMARY> cfg.members[]
{
"_id" : ,
"host" : "test2:27017",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : , #权重为1
"tags" : { },
"slaveDelay" : NumberLong(),
"votes" :
}
lianxi:PRIMARY> cfg.members[].priority = #设置权重为2 #重新加载配置
lianxi:PRIMARY> rs.reconfig(cfg)
{ "ok" : }
lianxi:PRIMARY> #点击回车
--06T14::04.701+ I NETWORK [thread1] trying reconnect to 127.0.0.1: (127.0.0.1) failed
--06T14::04.702+ I NETWORK [thread1] reconnect 127.0.0.1: (127.0.0.1) ok
lianxi:SECONDARY> #已经切换, #查看仲裁节点的日志记录
2018-11-06T14:50:53.166+0800 I NETWORK  [thread1] connection accepted from 10.0.102.214:42415 #8 (3 connections now open)
2018-11-06T14:50:53.166+0800 I -        [conn8] end connection 10.0.102.214:42415 (3 connections now open)
2018-11-06T14:50:53.170+0800 I REPL     [ReplicationExecutor] New replica set config in use: { _id: "lianxi", version: 4, protocolVersion: 1, members: [ { _id: 0, host: "test2:27017", arbiterOnly: false, buildIndexes: true, hidden: false, priority: 2.0, tags: {}, slaveDelay: 0, votes: 1 }, { _id: 1, host: "10.0.102.214:27017", arbiterOnly: false, buildIndexes: true, hidden: false, priority: 1.0, tags: {}, slaveDelay: 0, votes: 1 }, { _id: 2, host: "10.0.102.220:27017", arbiterOnly: true, buildIndexes: true, hidden: false, priority: 1.0, tags: {}, slaveDelay: 0, votes: 1 } ], settings: { chainingAllowed: true, heartbeatIntervalMillis: 2000, heartbeatTimeoutSecs: 10, electionTimeoutMillis: 10000, catchUpTimeoutMillis: 2000, getLastErrorModes: {}, getLastErrorDefaults: { w: 1, wtimeout: 0 }, replicaSetId: ObjectId('5be1232c50f7d2aeca0d5ab3') } }
2018-11-06T14:50:53.170+0800 I REPL     [ReplicationExecutor] This node is 10.0.102.220:27017 in the config
2018-11-06T14:50:53.171+0800 I -        [conn7] end connection 10.0.102.204:18288 (2 connections now open)
2018-11-06T14:50:53.172+0800 I NETWORK  [thread1] connection accepted from 10.0.102.204:18293 #9 (2 connections now open)
2018-11-06T14:50:53.172+0800 I NETWORK  [thread1] connection accepted from 10.0.102.204:18294 #10 (3 connections now open)
2018-11-06T14:50:53.172+0800 I NETWORK  [conn9] received client metadata from 10.0.102.204:18293 conn9: { driver: { name: "NetworkInterfaceASIO-Replication", version: "3.4.2" }, os: { type: "Linux", name: "CentOS release 6.6 (Final)", architecture: "x86_64", version: "Kernel 2.6.32-504.el6.x86_64" } }
2018-11-06T14:50:53.172+0800 I -        [conn10] end connection 10.0.102.204:18294 (3 connections now open)
2018-11-06T14:50:53.173+0800 I -        [conn9] end connection 10.0.102.204:18293 (2 connections now open)
2018-11-06T14:50:53.174+0800 I NETWORK  [thread1] connection accepted from 10.0.102.204:18295 #11 (2 connections now open)
2018-11-06T14:50:53.174+0800 I NETWORK  [conn11] received client metadata from 10.0.102.204:18295 conn11: { driver: { name: "NetworkInterfaceASIO-Replication", version: "3.4.2" }, os: { type: "Linux", name: "CentOS release 6.6 (Final)", architecture: "x86_64", version: "Kernel 2.6.32-504.el6.x86_64" } }
2018-11-06T14:51:03.177+0800 I NETWORK  [thread1] connection accepted from 10.0.102.204:18297 #12 (3 connections now open)
2018-11-06T14:51:03.177+0800 I NETWORK  [conn12] received client metadata from 10.0.102.204:18297 conn12: { driver: { name: "NetworkInterfaceASIO-Replication", version: "3.4.2" }, os: { type: "Linux", name: "CentOS release 6.6 (Final)", architecture: "x86_64", version: "Kernel 2.6.32-504.el6.x86_64" } }
2018-11-06T14:51:08.173+0800 I REPL     [ReplicationExecutor] Member 10.0.102.214:27017 is now in state SECONDARY
2018-11-06T14:51:08.173+0800 I REPL     [ReplicationExecutor] Member test2:27017 is now in state PRIMARY

上面讲述了如何搭建副本集,下面我们来着重说明副本集的工作原理:

副本集依赖两个基本的机制:oplog和heartbeat。oplog允许复制数据,heartbeat监控状态并触发灾备。

mongodb的副本集依赖oplog。oplog是一个固定的集合,它存在于每个复制节点的local数据库中,而且记录了所有的数据变化。

数据写入主节点,同样的操作记录会被写入到oplog日志里面,从节点读取oplog信息,然后开始复制数据,并且把复制的数据信息写入自己的oplog日志里面。(注意和mysq的主从区分)

从节点使用了长轮询的来立即应用从主节点复制的oplog记录。长轮询意味着从节点向主节点发送了长请求。当主节点接受修改时,它会立即响应等待请求。因此,从节点将几乎可以做到实时更新。

每个oplog项目通过bson时间戳来区分,并且所有的从节点使用时间戳来跟踪它们应用的最新项目。

lianxi:PRIMARY> use local
switched to db local
lianxi:PRIMARY> show tables;
me #用来实现写关注点
oplog.rs #副本集存储oplog日志的集合
replset.election #副本集的选举信息【不确定】
replset.minvalid #包含副本集成员的信息
startup_log #mongodb每次启动信息
system.replset #副本集的配置文档信息
lianxi:PRIMARY> db.oplog.rs.findOne({op: "i"}) #查看oplog的日志信息
{
    "ts" : Timestamp(1541484209, 2), #时间戳,时间戳的第二部分表示一个计数器,指定操作op的计数
    "t" : NumberLong(1), #【只想说一句,不知道的是不是都没解释,】
    "h" : NumberLong("1380699828208596822"),#
    "v" : 2,
    "op" : "i",
    "ns" : "mydb.cityinfo", #指定操作码,可以有“i”,“u”,“d”,“c【表示数据库命令】”,“db【声明当前数据库】”,“n:表示空操作”!
    "o" : { #操作的文档
        "_id" : ObjectId("5be12eb19efff2d99afed619"),
        "name" : "HongKong",
        "country" : "CHINA"
    }
}
lianxi:PRIMARY>
#系统会为每个更新的文档创建oplog记录

在oplog.rs中,mongodb会为每一个更改的文档创建一个oplog日志记录,然后从节点都会复制主节点的oplog。当某个从节点准备更新自己的数据时,它会做3件事:

检查自己当前oplog利最新记录的时间戳,然后轮询主节点的oplog的时间戳,若主节点的时间戳大于自己当前记录的时间戳,就会写入数据,并且把每个操作日志保存到自己的oplog里。

这样的原因是,可以把任何一个已经写入数据的从提升为主,因为每一个主都有一个oplog日志,这样其他的从节点可以复制。

oplog的日志操作是幂等性的!

Oplog日志的大小设置:

oplog是一个固定的集合,因此,在mongodb2.6中不允许创建后再修改其大小。在mongodb3.x里,可以修改oplog的大小。操作步骤是,停止mongodb实例,然后作为单独的节点启动,修改oplog大小,重新启动成员。

没有指定oplog日志大小时,其安装默认参数启动,默认大小因存储引擎不一样而不一样!

存储引擎 默认oplog大小 下限 上限
内存存储引擎 5%的物理内存 50MB 50G
WiredTiger存储引擎 5%的磁盘空间 990M 50G
MMAPv1存储引擎 5%的磁盘空间 990M 50G

手动设置日志大小时,可以使用oplogSizeMB参数在配置文件中指定来启动。

心跳监测:

副本集的心跳便于实现选举和灾备。默认情况下,每个副本集成员会每隔2秒ping一次索引供其他成员。这样,系统就可以判断自己的健康状态。当运行rs.status()时,查看每个节点的心跳和状态(1表示健康,0表示无应答)。

每个副本集必须保证一直只有一个节点存在!

副本集的管理

在上面配置副本集的时候使用rs.add(), rs.initiate()这些方法使用的很方便,但是他们隐藏了某些可复制集参数。

#创建配置文档,这个_id字段指向配置的副本集名字,members为副本集的成员
lianxi:PRIMARY> cfg = {_id: "lianxi", members: []}
{ "_id" : "lianxi", "members" : [ ] }
lianxi:PRIMARY> cfg.members.push({_id:, host: "10.0.102.204:27017"}) #添加成员1
1
lianxi:PRIMARY> cfg.members.push({_id:, host: "10.0.102.214:27017"}) #添加成员2 lianxi:PRIMARY> cfg.members.push({_id:, host: "10.0.102.220:27017", arbiterOnly: true}) #添加仲裁节点 lianxi:PRIMARY> cfg #查看文档参数
{
"_id" : "lianxi",
"members" : [
{
"_id" : ,
"host" : "10.0.102.204:27017"
},
{
"_id" : ,
"host" : "10.0.102.214:27017"
},
{
"_id" : ,
"host" : "10.0.102.220:27017",
"arbiterOnly" : true
}
]
}
lianxi:PRIMARY>rs.initiate(cfg) #把文档变量作为参数传递给initiate()函数 #虽然每个副本集有50个成员,但是它们只有7个投票成员。
rs.initiate()命令内部简单的封装了replSetInitiate,因此也可以使用如下命令:
db.runCommand({replSetInitiate: cfg}) ###修改了配置文档之后,我们需要重新加载配置文档,可以使用命令rs.reconfig(),这个命令同时还封装了replSetReconfig

查看副本集的运行状态

可以通过运行replSetGetStatus命令来查看副本集的运行状态,rs.status()封装了这个命令。

mongodb中副本集成员可能的状态值列表如下:

STARTUP:     表示副本集正在通过ping与其他节点成员写入并共享配置数据。
PRIMARY: 主节点,副本集中只有一个主节点。
SECONDARY: 从节点,只读节点。这个节点如果优先级大于0,并且没有被标记为hidden,故障时变为主节点。
RECOVERING: 此节点现在无法读写。通常在故障转移或者添加新节点后看到此状态、
FATAL: 网络仍在连接,但是此节点对ping没有响应。
UNKNOWN: 没有建立网络连接
ARBITER:   仲裁节点
DOWN: 节点可以访问,但是当前不响应心跳ping消息。
ROLLBACK: 正在回滚。
REMOVED: 该节点曾经是副本集成员,现在已经被删除了!
STARTUP2: 同步的初始状态。

mongodb-的副本集的更多相关文章

  1. [DataBase] MongoDB (8) 副本集

    MongoDB  创建副本集 MongoDB复制是将数据同步在多个服务器的过程. 复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性. 复制还允许您从 ...

  2. mongodb创建副本集命令

    mongodb创建副本集命令 ./mongod --replSet spock --dbpath ../data --smallfiles > config ={... "_id&qu ...

  3. MongoDB之副本集

    MongoDB之副本集 一.简介 MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介于关系数据库 ...

  4. MongoDB 复制(副本集)

    MongoDB复制是将数据同步在多个服务器的过程. 复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性. 复制还允许您从硬件故障和服务中断中恢复数据. ...

  5. Mongodb主从复制/ 副本集/分片集群介绍

    前面的文章介绍了Mongodb的安装使用,在 MongoDB 中,有两种数据冗余方式,一种 是 Master-Slave 模式(主从复制),一种是 Replica Sets 模式(副本集). Mong ...

  6. MongoDB 搭建副本集

    副本集(Replica Set)是一组MongoDB实例组成的集群,由一个主(Primary)服务器和多个备份(Secondary)服务器构成.通过Replication,将数据的更新由Primary ...

  7. [原创]在Docker上部署mongodb分片副本集群。

    一.安装docker. 请参考:http://www.cnblogs.com/hehexiaoxia/p/6150584.html 二.编写dockerfile. 1.在根目录下创建mongod的do ...

  8. mongodb(副本集)

    副本集是mongo下的一种集群配置方式: 1.通过oplog的方式将主节点数据同步到副本节点,oplog不记录查询语句(因为不改变数据): 2.mongo的副本集可以有一个主节点,多个副本节点,主节点 ...

  9. MongoDb的副本集搭建教程(个人操作笔记)

    很多公司都在用MongoDb ,一直没有时间研究,最近好好的整了一下,做下笔记,直接上操作步骤,关于Mongodb的理论知识可以搜索其他资料,也可以联系我索取 mongoDB官方已经不建议使用主从模式 ...

  10. mongodb系列~mongodb的副本集(1)

    一 简介: mongodb副本集 二 复制方式: 1 全量复制 2 增量复制三 同步检测过程:    一 正常情况下:       1 master执行语句,并将所有的修改数据库的操作以日志Oplog ...

随机推荐

  1. jdbc链接数据库,获取表名,字段名和数据

    import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; import  ...

  2. java三方---->html解析jsoup的使用

    jsoup 是一款 Java 的HTML 解析器,可直接解析某个URL地址.HTML文本内容.它提供了一套非常省力的API,可通过DOM,CSS以及类似于JQuery的操作方法来取出和操作数据.今天我 ...

  3. Dropwizard简单入门

    Dropwizard:一个简洁的RESTful Web框架 Dropwizard跨越了开发库与框架的界限,旨在为Web应用所需的功能提供高性能.可靠的实现.Dropwizard将这些功能抽象为可重用的 ...

  4. 最小圆覆盖(随机增量法&模拟退火法)

    http://acm.hdu.edu.cn/showproblem.php?pid=3007 相关题型连接: http://acm.hdu.edu.cn/showproblem.php?pid=393 ...

  5. SpringMVC中使用@ResponseBody注解将任意POJO对象返回值转换成json进行返回

    @ResponseBody 作用: 该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区. ...

  6. react路由守卫

    react没有vue那样的路由钩子beforeEach,实现登陆验证. 实现效果:如果没有登陆,就跳转到登陆界面,如果登陆过浏览器存有登陆信息就跳转到所输入的路由界面,如果该路由不存在则跳到404页面 ...

  7. 用Squid实现反向代理

    Last-Modified: 告诉反向代理页面什么时间被修改 Expires: 告诉反向代理页面什么时间应该从缓冲区中删除 Cache-Control: 告诉反向代理页面是否应该被缓冲 Pragma: ...

  8. MVC学习之HtmlHelper

    1.为什么要使用HtmlHelper? 1.首先HtmlHelper是一个类型,MVC中的ViewPage<TModel>中的一个属性Html属性,这个属性的类型就是HtmlHelper& ...

  9. Servlet------>jsp EL表达式

    取值: ${data}------>pageContext.findAttribute("data"); ${data.name}------>data.getName ...

  10. ubuntu ibus ,chinese input-method

    第一:安装IBus框架, sudo apt-get install ibus ibus-clutter ibus-gtk ibus-gtk3 ibus-qt4 启动IBus框架,在终端输入: im-s ...