您的位置:首页 > 数据库 > Mongodb

mongodb replica sets复制集详解

2016-06-07 21:55 579 查看


一.replicasets介绍

一个复制集是一组包含相同数据集的mongod实例.一个复制集只能有一个是primary节点,其它的节点为secondary节点.

和主从复制的原理一样,复制集也是通过读取oplog来进行数据传输.oplog是一个cappedcollection即固定表,创建表的时候可以指定其大小,当oplog满的时候会删除旧的数据.所以设置oplog的大小非常重要,如果oplog在primary节点被覆盖而尚未被secondary节点读取的话就要重新resync.

一般的使用replicasets复制集使用如下架构,一主一备,还有一个仲裁负责进行failover.仲裁主机不存放数据.





复制集使用的异步同步方式,复制集成员直接每隔2s发送一次hearbeat(pings).当主节点与其它成员通信超时10s后,一个secondary节点将会被选举为primary节点.在新的版本中,如果存在多个secondary节点,当第一个节点被选举为primary后,其它的secondary节点将从它开始复制数据.


二.创建replicasets

下面是搭建一个一主两从三节点replicasets的具体步骤


(1).添加replSet参数

在你所有节点的mongodb实例添加replSet参数,在一个group中的replSet参数需要一致,有点类似于oracledg中的db_name参数.

然后启动所有节点的mongodb实例

例如我的主机名为mongodb1的配置文件:

[root@mongodb1~]#cat/etc/mongod.conf
port=27017
dbpath=/data/db
logpath=/data/log/mongod.log
fork=true
oplogSize=500
replSet=rs0
[/code]

然后启动所有的实例:

[root@mongodb1~]#mongod-f/etc/mongod.conf
abouttoforkchildprocess,waitinguntilserverisreadyforconnections.
forkedprocess:2722
childprocessstartedsuccessfully,parentexiting
[/code]

(2).初始化replicaset

使用rs.initiate()来初始化复制集,在一个节点而且只能在一个节点进行初始化,例如我们这里在mongodb1上进行初始化,那么这个节点将会成为primary节点.

这里我们没有配置config参数文件,那么系统会使用默认的配置文件.

>rs.initiate()
{
"info2":"noconfigurationspecified.Usingadefaultconfigurationfortheset",
"me":"mongodb1:27017",
"ok":1
}
rs0:OTHER>
[/code]

当然你也可以先配置一个配置文件,然后使用rs.initiate(rsconf)来初始化,例如:

rsconf={
_id:"rs0",
members:[
{
_id:0,
host:"<hostname>:27017"
}
]
}
[/code]

(3).检查初始化配置文件

使用rs.conf()来查看初始化配置文件:

rs0:OTHER>rs.conf()
{
"_id":"rs0",
"version":1,
"protocolVersion":NumberLong(1),
"members":[
{
"_id":0,
"host":"mongodb1:27017",
"arbiterOnly":false,
"buildIndexes":true,
"hidden":false,
"priority":1,
"tags":{
},
"slaveDelay":NumberLong(0),
"votes":1
}
],
"settings":{
"chainingAllowed":true,
"heartbeatIntervalMillis":2000,
"heartbeatTimeoutSecs":10,
"electionTimeoutMillis":10000,
"getLastErrorModes":{
},
"getLastErrorDefaults":{
"w":1,
"wtimeout":0
},
"replicaSetId":ObjectId("575647b35e9005faa0e8d690")
}
}
[/code]

可以看到当前的members只有一个,即_id为0的,即我们刚刚初始化的这个节点.

(4).将剩下的成员添加到复制集

使用rs.add()方法将成员添加到复制集.你必须连接到primary节点来执行添加成员的操作.你可以使用rs.status()查看主节点信息:

rs0:PRIMARY>rs.status()
{
"set":"rs0",
"date":ISODate("2016-06-07T04:16:20.542Z"),
"myState":1,
"term":NumberLong(1),
"heartbeatIntervalMillis":NumberLong(2000),
"members":[
{
"_id":0,
"name":"mongodb1:27017",
"health":1,
"state":1,
"stateStr":"PRIMARY",
"uptime":1184,
"optime":{
"ts":Timestamp(1465272244,1),
"t":NumberLong(1)
},
"optimeDate":ISODate("2016-06-07T04:04:04Z"),
"electionTime":Timestamp(1465272243,2),
"electionDate":ISODate("2016-06-07T04:04:03Z"),
"configVersion":1,
"self":true
}
],
"ok":1
}
[/code]

添加成员:

rs0:PRIMARY>rs.add("mongodb2:27017")
{"ok":1}
rs0:PRIMARY>rs.add("mongodb3:27017")
{"ok":1}
[/code]

再次查看复制集配置,可以看到members有三个成员了.

rs0:PRIMARY>rs.conf()
{
"_id":"rs0",
"version":3,
"protocolVersion":NumberLong(1),
"members":[
{
"_id":0,
"host":"mongodb1:27017",
"arbiterOnly":false,
"buildIndexes":true,
"hidden":false,
"priority":1,
"tags":{
},
"slaveDelay":NumberLong(0),
"votes":1
},
{
"_id":1,
"host":"mongodb2:27017",
"arbiterOnly":false,
"buildIndexes":true,
"hidden":false,
"priority":1,
"tags":{
},
"slaveDelay":NumberLong(0),
"votes":1
},
{
"_id":2,
"host":"mongodb3:27017",
"arbiterOnly":false,
"buildIndexes":true,
"hidden":false,
"priority":1,
"tags":{
},
"slaveDelay":NumberLong(0),
"votes":1
}
],
"settings":{
"chainingAllowed":true,
"heartbeatIntervalMillis":2000,
"heartbeatTimeoutSecs":10,
"electionTimeoutMillis":10000,
"getLastErrorModes":{
},
"getLastErrorDefaults":{
"w":1,
"wtimeout":0
},
"replicaSetId":ObjectId("575647b35e9005faa0e8d690")
}
}
[/code]

查看复制集状态,可以看到mongodb1是primary节点,其它两个是secondary节点:

rs0:PRIMARY>rs.status()
{
"set":"rs0",
"date":ISODate("2016-06-07T04:28:12.721Z"),
"myState":1,
"term":NumberLong(1),
"heartbeatIntervalMillis":NumberLong(2000),
"members":[
{
"_id":0,
"name":"mongodb1:27017",
"health":1,
"state":1,
"stateStr":"PRIMARY",
"uptime":1896,
"optime":{
"ts":Timestamp(1465273627,1),
"t":NumberLong(1)
},
"optimeDate":ISODate("2016-06-07T04:27:07Z"),
"electionTime":Timestamp(1465272243,2),
"electionDate":ISODate("2016-06-07T04:04:03Z"),
"configVersion":3,
"self":true
},
{
"_id":1,
"name":"mongodb2:27017",
"health":1,
"state":2,
"stateStr":"SECONDARY",
"uptime":73,
"optime":{
"ts":Timestamp(1465273627,1),
"t":NumberLong(1)
},
"optimeDate":ISODate("2016-06-07T04:27:07Z"),
"lastHeartbeat":ISODate("2016-06-07T04:28:11.197Z"),
"lastHeartbeatRecv":ISODate("2016-06-07T04:28:12.206Z"),
"pingMs":NumberLong(0),
"syncingTo":"mongodb1:27017",
"configVersion":3
},
{
"_id":2,
"name":"mongodb3:27017",
"health":1,
"state":2,
"stateStr":"SECONDARY",
"uptime":65,
"optime":{
"ts":Timestamp(1465273627,1),
"t":NumberLong(1)
},
"optimeDate":ISODate("2016-06-07T04:27:07Z"),
"lastHeartbeat":ISODate("2016-06-07T04:28:11.198Z"),
"lastHeartbeatRecv":ISODate("2016-06-07T04:28:09.224Z"),
"pingMs":NumberLong(0),
"configVersion":3
}
],
"ok":1
}
[/code]

我们到mongodb2节点查看日志,发现我们在进行rs.add()的时候,系统自动进行了初始化:

2016-06-07T12:26:57.846+0800IREPL[ReplicationExecutor]Thisnodeismongodb2:27017intheconfig
2016-06-07T12:26:57.846+0800IREPL[ReplicationExecutor]transitiontoSTARTUP2
2016-06-07T12:26:57.846+0800IREPL[rsSync]******
2016-06-07T12:26:57.846+0800IREPL[rsSync]creatingreplicationoplogofsize:500MB...
2016-06-07T12:26:57.846+0800IREPL[ReplicationExecutor]Membermongodb1:27017isnowinstatePRIMARY
2016-06-07T12:26:57.858+0800ISTORAGE[rsSync]StartingWiredTigerRecordStoreThreadlocal.oplog.rs
2016-06-07T12:26:57.858+0800ISTORAGE[rsSync]Thesizestorerreportsthattheoplogcontains0recordstotalingto0bytes
2016-06-07T12:26:57.858+0800ISTORAGE[rsSync]Scanningtheoplogtodeterminewheretoplacemarkersfortruncation
2016-06-07T12:26:57.922+0800IREPL[rsSync]******
2016-06-07T12:26:57.922+0800IREPL[rsSync]initialsyncpending
2016-06-07T12:26:57.940+0800IREPL[ReplicationExecutor]syncingfrom:mongodb1:27017
2016-06-07T12:26:57.943+0800IREPL[rsSync]initialsyncdropalldatabases
2016-06-07T12:26:57.943+0800ISTORAGE[rsSync]dropAllDatabasesExceptLocal2
2016-06-07T12:26:57.966+0800IREPL[rsSync]initialsyncclonealldatabases
2016-06-07T12:26:57.967+0800IREPL[rsSync]initialsynccloningdb:suq
...
...
...
2016-06-07T12:27:00.234+0800IREPL[rsSync]oplogsync3of3
2016-06-07T12:27:00.235+0800IREPL[rsSync]initialsyncfinishingup
2016-06-07T12:27:00.235+0800IREPL[rsSync]setminValid=(term:1,timestamp:Jun712:26:58:1)
2016-06-07T12:27:00.240+0800IREPL[rsSync]initialsyncdone
2016-06-07T12:27:00.241+0800IREPL[ReplicationExecutor]transitiontoRECOVERING
2016-06-07T12:27:00.242+0800IREPL[ReplicationExecutor]transitiontoSECONDARY
[/code]

此时登录到mongodb2上发现提示符也变成了secondary,查看数据也全部同步过来了:

rs0:SECONDARY>usesuq
switchedtodbsuq
rs0:SECONDARY>rs.slaveOk()
rs0:SECONDARY>showcollections
t1
test2
test3
test4
test5
test6
test7
[/code]

到此replicasets的搭建就结束了,还是蛮简单的.


三.复制集参数


(1)参数介绍

这里介绍rs.conf()里的参数,例如下面这个config内容:

rs0:PRIMARY>rs.conf()
{
"_id":"rs0",
"version":3,
"protocolVersion":NumberLong(1),
"members":[
{
"_id":0,
"host":"mongodb1:27017",
"arbiterOnly":false,
"buildIndexes":true,
"hidden":false,
"priority":1,
"tags":{
},
"slaveDelay":NumberLong(0),
"votes":1
},
{
"_id":1,
"host":"mongodb2:27017",
"arbiterOnly":false,
"buildIndexes":true,
"hidden":false,
"priority":1,
"tags":{
},
"slaveDelay":NumberLong(0),
"votes":1
},
{
"_id":2,
"host":"mongodb3:27017",
"arbiterOnly":false,
"buildIndexes":true,
"hidden":false,
"priority":1,
"tags":{
},
"slaveDelay":NumberLong(0),
"votes":1
}
],
"settings":{
"chainingAllowed":true,
"heartbeatIntervalMillis":2000,
"heartbeatTimeoutSecs":10,
"electionTimeoutMillis":10000,
"getLastErrorModes":{
},
"getLastErrorDefaults":{
"w":1,
"wtimeout":0
},
"replicaSetId":ObjectId("575647b35e9005faa0e8d690")
}
}
[/code]

_id:是用来标识复制集,参数内容和数据库启动的时候设置的rplSet参数一致

version:用来表示config参数的新旧,每次修改了config然后使用rs.reconfig重新配置的时候version的值会自动加1

protocolversion:是协议版本

members是一个数组,数组成员表示每个节点的信息.下面结束members的主要内容.

_id:用来标识节点号,唯一

host:表示节点的地址和端口信息

arbiterOnly:这是一个bool型,默认为false,用来表示这个节点是否是arbiter节点,只是用来投票

buildIndexes:这也是一个bool型,默认为true.用来表示同步的时候是否同步索引.一般设置为true.如果要设置为false,则必须将priority设置为0

hidden:这也是bool型,默认问false,用来表示这个节点是否为隐藏节点,如果是隐藏节点将不对外服务,只是单纯的同步信息,而且如果设置为了隐藏节点,使用rs.isMaster()方法将无法查看到隐藏节点的信息,但是可以使用rs.status()查看.设置隐藏节点必须首先将节点的priority设置为0

priority:表示权重,默认为1,如果将priority设置为0那么这个节点将永远无法成为primary节点,现在新的版本可以设置为超过1的数

slaveDelay:复制延迟,这个是整数,单位为秒,用来设置复制的延时.一般用来防止误操作,延迟节点必须优先级设置为0,hidden设置为true,然后设置slaveDelay值,

votes:表示这个节点是否有权利进行投票.

tags:表示标记,例如可以标记这个节点的作用等

settings是一些配置信息

chainingAllowed:表示是否允许链式复制,即某个secondary可以作为其它的secondary的源,默认是true.

heartbeatIntervalMillis:表示heartbeat的间隔时间,默认是没个两秒钟发送一个hearbeat包.

heartbeatTimeoutSecs:表示心跳检测超时时间,默认是10秒.

electionTimeoutMillis:表示选举超时时间,默认是10秒.


(2)参数修改

详细的修改参数参考官方文档:https://docs.mongodb.com/manual/administration/replica-set-member-configuration/

修改复制集的参数一般分为三步:

1.将当前的参数进行复制

rs0:PRIMARY>rsconfig=rs.conf()
[/code]

2.修改参数

例如我要将节点_id为2的主机的权限设置为0.5

rs0:PRIMARY>rsconfig.members[2].priority=0.5
0.5
[/code]

3.重新reconfig设置

rs0:PRIMARY>rs.reconfig(rsconfig)
{"ok":1}
[/code]

最后重新查看配置文件:

{
"_id":2,
"host":"mongodb3:27017",
"arbiterOnly":false,
"buildIndexes":true,
"hidden":false,
"priority":0.5,
"tags":{
},
"slaveDelay":NumberLong(0),
"votes":1
}
[/code]


四.复制集管理


Replicatemethods方法

可以使用rs.help()来查看所有的复制方法

rs0:SECONDARY>rs.help()
	rs.status(){replSetGetStatus:1}checksreplsetstatus
	rs.initiate(){replSetInitiate:null}initiatessetwithdefaultsettings
	rs.initiate(cfg){replSetInitiate:cfg}initiatessetwithconfigurationcfg
rs.conf()getthecurrentconfigurationobjectfromlocal.system.replset
rs.reconfig(cfg)updatestheconfigurationofarunningreplicasetwithcfg(disconnects)
rs.add(hostportstr)addanewmembertothesetwithdefaultattributes(disconnects)
rs.add(membercfgobj)addanewmembertothesetwithextraattributes(disconnects)
rs.addArb(hostportstr)addanewmemberwhichisarbiterOnly:true(disconnects)
rs.stepDown([stepdownSecs,catchUpSecs])stepdownasprimary(disconnects)
rs.syncFrom(hostportstr)makeasecondarysyncfromthegivenmember
rs.freeze(secs)makeanodeineligibletobecomeprimaryforthetimespecified
rs.remove(hostportstr)removeahostfromthereplicaset(disconnects)
rs.slaveOk()allowqueriesonsecondarynodes
	rs.printReplicationInfo()checkoplogsizeandtimerange
	rs.printSlaveReplicationInfo()checkreplicasetmembersandreplicationlag
	db.isMaster()checkwhoisprimary
	reconfigurationhelpersdisconnectfromthedatabasesotheshellwilldisplay
	anerror,evenifthecommandsucceeds.
[/code]

详细可以参考官方文档:https://docs.mongodb.com/manual/reference/method/js-replication/

下面的一些修改操作一般都必须在primary节点上执行.


(1)rs.conf查看配置文件

前面已经介绍过了,使用rs.conf()来查看复制集参数


(2)rs.status查看复制集状态

前面已经介绍了一个使用rs.status()查看.

还可以使用rs.isMaster()查看节点是否是master节点:

rs0:SECONDARY>rs.isMaster()
{
	"hosts":[
"mongodb1:27017",
"mongodb2:27017",
"mongodb3:27017"
],
	"setName":"rs0",
	"setVersion":4,
	"ismaster":false,
	"secondary":true,
	"primary":"mongodb1:27017",
	"me":"mongodb3:27017",
	"maxBsonObjectSize":16777216,
	"maxMessageSizeBytes":48000000,
	"maxWriteBatchSize":1000,
	"localTime":ISODate("2016-06-07T12:08:44.914Z"),
	"maxWireVersion":4,
	"minWireVersion":0,
"ok":1
}
[/code]

(3)rs.add添加节点

使用rs.add([host],[arbiterOnly])方法来添加节点到复制集,这个方法有两个参数,第一个参数是host,第二个参数是arbiterOnly,默认为false,当为true的时候表示添加的节点只是用来作为仲裁节点.

在3.0以前的版本你需要手工指定_id,3.0以后的版本则不需要,系统会自动添加,下面是常用的语句:

rs.add('mongodb3.example.net:27017')
rs.add({host:"mongodb3:27017",priority:0})
rs.add("mongodb3:27010",true)
[/code]

rs0:PRIMARY>rs.add({"host":"mongodb3:27017","priority":0,"hidden":true})
{"ok":1}
[/code]


(4)rs.remove删除节点

rs.remove()就一个参数hostname:

rs0:PRIMARY>rs.remove("mongodb3:27017")
{"ok":1}
[/code]


(5)rs.addArb添加投票节点

rs.addArb()同样可以添加投票节点,也只有一个参数为hostname:

rs0:PRIMARY>rs.addArb("mongodb3:27017")
{"ok":1}
[/code]

(6)rs.freeze()将节点冻结

rs.freeze()就一个参数,为时间单位秒,表示在多长时间内将此节点冻结,即无法成为primary节点


(7)查看复制延时

rs.printSlaveReplicationInfo()

rs0:PRIMARY>rs.printReplicationInfo()
configuredoplogsize:500MB
loglengthstarttoend:30418secs(8.45hrs)
oplogfirsteventtime:TueJun07201612:04:03GMT+0800(CST)
oploglasteventtime:TueJun07201620:31:01GMT+0800(CST)
now:TueJun07201620:39:09GMT+0800(CST)
[/code]

rs.printSlaveReplicationInfo()

rs0:SECONDARY>rs.printSlaveReplicationInfo()
source:mongodb2:27017
	syncedTo:TueJun07201620:31:01GMT+0800(CST)
	0secs(0hrs)behindtheprimary
[/code]

结果输出都比较直观,记录了oplog的时间戳,同步延时等信息.


(8)rs.reconfig重新配置参数

rs.reconfig(configuration,force),上面已经介绍了重新配置参数的方法,需要注意的是,在某些情况下执行rs.reconfig()有可能触发将当前的primary降级为secondary节点.

当primary降级为secondary的的时候会强制关闭所有的客户端连接,然后花费几分钟时间将secondary节点选举为primary节点,在重新配置期间,数据库将处于不可操作状态.

特别需要注意的是当你指定的force为true的时候有可能会将一个已经提交的写操作回滚掉,因此需要谨慎的使用.


(9)rs.stepDown降级primary

rs.stepDown(stepDownSecs,secondaryCatchUpPeriodSecs)方法将primary节点降级为secondary节点,secondary节点会推选出一个升级为primary节点.此方法有两个参数,

第一个参数表示在多少时间内将降级primary,也表示这在此期间此节点无法变为primary,如果你指定了一个非number类型,则默认为60s

第二个参数表示mongod将等待多长时间让一个secondary提升为primary.默认为10s,第一个参数必须大于第二个参数.

rs0:PRIMARY>rs.stepDown()
2016-06-07T20:58:53.657+0800EQUERY[thread1]Error:errordoingquery:failed:networkerrorwhileattemptingtoruncommand'replSetStepDown'onhost'127.0.0.1:27017':
DB.prototype.runCommand@src/mongo/shell/db.js:135:1
DB.prototype.adminCommand@src/mongo/shell/db.js:153:16
rs.stepDown@src/mongo/shell/utils.js:1181:12
@(shell):1:1
2016-06-07T20:58:53.660+0800INETWORK[thread1]tryingreconnectto127.0.0.1:27017(127.0.0.1)failed
2016-06-07T20:58:53.662+0800INETWORK[thread1]reconnect127.0.0.1:27017(127.0.0.1)ok
rs0:SECONDARY>
rs0:SECONDARY>
[/code]

需要注意的是stepDown的过程中所有的客户端连接都会断开,会终止一些用户操作,例如创建索引.为了避免回滚,设置第二个参数,让mongod等待一段时间.

如果没有可用的secondary,那么会抛出一个异常.


(10)rs.syncFrom()

rs.syncFrom()可以让管理员里手工设置默认的复制源.参数为["hostname:port"]

在3.2版本中一个没有投票权的节点无法作为一个拥有投票权的节点的源.是否拥有投票权查看rs.conf的vote参数.

在rs.status状态中有一个状态为syncingTO表示此节点是从那个节点同步数据,例如:

{
"_id":2,
"name":"mongodb3:27017",
"health":1,
"state":2,
"stateStr":"SECONDARY",
"uptime":33316,
"optime":{
"ts":Timestamp(1465304797,1),
"t":NumberLong(2)
},
"optimeDate":ISODate("2016-06-07T13:06:37Z"),
"syncingTo":"mongodb2:27017",
"configVersion":14,
"self":true
}
[/code]

可以看到原来_id为2的节点是从mongodb2(primary)同步数据,我们可以修改他从其它的secondary(mongodb1)来同步数据:

rs0:SECONDARY>rs.syncFrom("mongodb1:27017")
{
	"syncFromRequested":"mongodb1:27017",
	"prevSyncTarget":"mongodb2:27017",
"ok":1
}
[/code]

而且可以可以在日志里面看到如下信息:

2016-06-07T21:10:55.416+0800IREPL[ReplicationExecutor]syncingfrom:mongodb1:27017byrequest
2016-06-07T21:10:55.418+0800IREPL[SyncSourceFeedback]settingsyncSourceFeedbacktomongodb1:27017
2016-06-07T21:10:55.419+0800IASIO[NetworkInterfaceASIO-BGSync-0]Successfullyconnectedtomongodb1:27017
2016-06-07T21:11:17.124+0800IASIO[NetworkInterfaceASIO-BGSync-0]Successfullyconnectedtomongodb1:27017

[/code]

复制维护


(1)修改oplog大小

具体方法见文档:

https://docs.mongodb.com/manual/tutorial/change-oplog-size/

基本思路就是将一个secondary变为单机模式,然后重建local数据库下的oplog.rs集合,在重建的时候需要注意保留最新数据,方法是使用临时表做中间表.


(2)强制将节点转换为primary

具体方法见文档:

https://docs.mongodb.com/manual/tutorial/force-member-to-be-primary/

基本思路就是将节点的优先级调高,然后reconfig


(3)重新同步节点

具体方法见文档:https://docs.mongodb.com/manual/tutorial/resync-replica-set-member/

可以使用resync命令:

rs0:SECONDARY>db.runCommand({"resync":1})
{"ok":1}
[/code]

如果有多个secondary,可以拷贝其它正常的secondary数据.

还可以先将节点remove,然后删除dbpath下的文件,然后重新add,那么系统会自动进行resync.


(3)修改主机名

详细见文档:https://docs.mongodb.com/manual/tutorial/change-hostnames-in-a-replica-set/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: