Mongodb从0到1系列六: 复制集

来源:互联网 发布:知乎怎么用 编辑:程序博客网 时间:2024/05/21 15:00

Mongodb从0到1系列一:下载、安装、启动、停止

Mongodb从0到1系列二:数据库与集合操作文档、增删改查

Mongodb从0到1系列三: 条件查询、大小写

Mongodb从0到1系列四: Limit与Skip方法、排序、索引

 Mongodb从0到1系列五: 主从复制


11 复制集

复制集是一个带有故障转移的主从集群。是从现有的主从模式演变而来,增加了自动故障转移和节点成员自动恢复。
复制集模式中没有固定的主结点,在启动后,多个服务节点间将自动选举产生一个主结点。该主结点被称为primary,一个或多个从结点被称为secondaries。primary结点基本上就是master结点,不同之处在于primary结点在不同时间可能是不同的服务器。如果当前的主结点失效了,复制集中的其余结点将会试图选出一个新的主结点。
复制集模式的好处是,一切自动化。首先,复制集模式本身做了大量的管理工作,自动管理从节点,确保数据不会不一致。其次,主节点挂掉后,会自动判断集群中的服务器并进行故障转移,推举新的主节点。

Primary 主节点,一个复制集有且仅有一台服务器处于Primary状态,只有主节点才对外提供读写服务。如果主节点挂掉,复制集将会投票选出一个备用节点成为新的主节点。
Secondary 备用节点,复制集允许有多台Secondary,每个备用节点的数据与主节点的数据是完全同步的。
Recovering 恢复中,当复制集中某台服务器挂掉或者掉线后数据无法同步,重新恢复服务后从其他成员复制数据,这时就处于恢复过程,数据同步后,该节点又回到备用状态。
Arbiter 仲裁节点,该类节点可以不用单独存在,如果配置为仲裁节点,就主要负责在复本集中监控其他节点状态,投票选出主节点。该节点将不会用于存放数据。如果没有仲裁节点,那么投票工作将由所有节点共同进行。
Down 无效节点,当服务器挂掉或掉线时就会处于该状态。

11.1 有仲裁节点

由于机器数量限制,只在一个节点上做测试,使用3个不同的dbpath和端口, 开3个sesion, 指定同一个replSet

db2a:~ # mkdir /data/repdb2
db2a:~ # mkdir /data/repdb3
db2a:~ # mongod --dbpath=/data/db --port 27017 --replSet rs1
db2a:~ # mongod --dbpath=/data/repdb2 --port 27018 --replSet rs1
db2a:~ # mongod --dbpath=/data/repdb3 --port 27019 --replSet rs1

新开一mongo shell Session:
//配置复制集,27019作为仲裁节点:
db2a:~ # mongo 192.168.37.1:27017
MongoDB Enterprise > config = {_id:"rs1", members:[{_id:0,host:"192.168.37.1:27017"}, {_id:1,host:"192.168.37.1:27018"}, {_id:2,host:"192.168.37.1:27019", arbiterOnly:true}]}
{
        "_id" : "rs1",
        "members" : [
                {
                        "_id" : 0,
                        "host" : "192.168.37.1:27017"
                },
                {
                        "_id" : 1,
                        "host" : "192.168.37.1:27018"
                },
                {
                        "_id" : 2,
                        "host" : "192.168.37.1:27019",
                        "arbiterOnly" : true
                }
        ]
}
MongoDB Enterprise > rs.initiate(config)
{ "ok" : 1 }

查看复制集配置:
MongoDB Enterprise rs1:SECONDARY> rs.conf()
{        "_id" : "rs1",        "version" : 1,        "protocolVersion" : NumberLong(1),        "members" : [                {                        "_id" : 0,                        "host" : "192.168.37.1:27017",                        "arbiterOnly" : false,                        "buildIndexes" : true,                        "hidden" : false,                        "priority" : 1,                        "tags" : {                        },                        "slaveDelay" : NumberLong(0),                        "votes" : 1                },                {                        "_id" : 1,                        "host" : "192.168.37.1:27018",                        "arbiterOnly" : false,                        "buildIndexes" : true,                        "hidden" : false,                        "priority" : 1,                        "tags" : {                        },                        "slaveDelay" : NumberLong(0),                        "votes" : 1                },                {                        "_id" : 2,                        "host" : "192.168.37.1:27019",                        "arbiterOnly" : true,                        "buildIndexes" : true,                        "hidden" : false,                        "priority" : 1,                        "tags" : {                        },                        "slaveDelay" : NumberLong(0),                        "votes" : 1                }        ],        "settings" : {                "chainingAllowed" : true,                "heartbeatIntervalMillis" : 2000,                "heartbeatTimeoutSecs" : 10,                "electionTimeoutMillis" : 10000,                "catchUpTimeoutMillis" : 60000,                "getLastErrorModes" : {                },                "getLastErrorDefaults" : {                        "w" : 1,                        "wtimeout" : 0                },                "replicaSetId" : ObjectId("59740afc483597f1b29ddd33")        }}

查看复制集状态,可以看到
27017的 "stateStr" : "PRIMARY"
27018的 "stateStr" : "SECONDARY"
27019的 "stateStr" : "ARBITER"

MongoDB Enterprise rs1:PRIMARY> rs.status()
{        "set" : "rs1",        "date" : ISODate("2017-07-23T02:38:06.598Z"),        "myState" : 1,        "term" : NumberLong(1),        "heartbeatIntervalMillis" : NumberLong(2000),        "optimes" : {                "lastCommittedOpTime" : {                        "ts" : Timestamp(1500777485, 1),                        "t" : NumberLong(1)                },                "appliedOpTime" : {                        "ts" : Timestamp(1500777485, 1),                        "t" : NumberLong(1)                },                "durableOpTime" : {                        "ts" : Timestamp(1500777485, 1),                        "t" : NumberLong(1)                }        },        "members" : [                {                        "_id" : 0,                        "name" : "192.168.37.1:27017",                        "health" : 1,                        "state" : 1,                        "stateStr" : "PRIMARY",                        "uptime" : 340,                        "optime" : {                                "ts" : Timestamp(1500777485, 1),                                "t" : NumberLong(1)                        },                        "optimeDate" : ISODate("2017-07-23T02:38:05Z"),                        "electionTime" : Timestamp(1500777223, 1),                        "electionDate" : ISODate("2017-07-23T02:33:43Z"),                        "configVersion" : 1,                        "self" : true                },                {                        "_id" : 1,                        "name" : "192.168.37.1:27018",                        "health" : 1,                        "state" : 2,                        "stateStr" : "SECONDARY",                        "uptime" : 274,                        "optime" : {                                "ts" : Timestamp(1500777475, 1),                                "t" : NumberLong(1)                        },                        "optimeDurable" : {                                "ts" : Timestamp(1500777475, 1),                                "t" : NumberLong(1)                        },                        "optimeDate" : ISODate("2017-07-23T02:37:55Z"),                        "optimeDurableDate" : ISODate("2017-07-23T02:37:55Z"),                        "lastHeartbeat" : ISODate("2017-07-23T02:38:05.508Z"),                        "lastHeartbeatRecv" : ISODate("2017-07-23T02:38:05.068Z"),                        "pingMs" : NumberLong(0),                        "syncingTo" : "192.168.37.1:27017",                        "configVersion" : 1                },                {                        "_id" : 2,                        "name" : "192.168.37.1:27019",                        "health" : 1,                        "state" : 7,                        "stateStr" : "ARBITER",                        "uptime" : 274,                        "lastHeartbeat" : ISODate("2017-07-23T02:38:05.508Z"),                        "lastHeartbeatRecv" : ISODate("2017-07-23T02:38:04.268Z"),                        "pingMs" : NumberLong(0),                        "configVersion" : 1                }        ],        "ok" : 1}

查看当前连接Server状态:发现是primary:
MongoDB Enterprise rs1:PRIMARY> rs.isMaster()
{        "hosts" : [                "192.168.37.1:27017",                "192.168.37.1:27018"        ],        "arbiters" : [                "192.168.37.1:27019"        ],        "setName" : "rs1",        "setVersion" : 1,        "ismaster" : true,        "secondary" : false,        "primary" : "192.168.37.1:27017",        "me" : "192.168.37.1:27017",...}

同时,可以分别在session1,2,3里看到以下信息:
Session1:
2017-07-23T10:33:43.277+0800 I REPL     [ReplicationExecutor] transition to PRIMARY
------------
Session2:
2017-07-23T10:33:34.796+0800 I REPL     [rsSync] transition to RECOVERING
2017-07-23T10:33:34.798+0800 I REPL     [rsBackgroundSync] could not find member to sync from
2017-07-23T10:33:34.803+0800 I REPL     [rsSync] transition to SECONDARY
------------
Session3:
2017-07-23T10:33:34.133+0800 I REPL     [ReplicationExecutor] transition to ARBITER

连接到27018,查询状态,看到不是primary:
MongoDB Enterprise rs1:PRIMARY> exit
bye
db2a:~ # mongo 192.168.37.1:27018
MongoDB Enterprise rs1:SECONDARY> rs.isMaster()
{        "hosts" : [                "192.168.37.1:27017",                "192.168.37.1:27018"        ],        "arbiters" : [                "192.168.37.1:27019"        ],        "setName" : "rs1",        "setVersion" : 1,        "ismaster" : false,        "secondary" : true,        "primary" : "192.168.37.1:27017",        "me" : "192.168.37.1:27018",...}

查询数据,发现已经同步过来:
MongoDB Enterprise rs1:SECONDARY> rs.slaveOk()
MongoDB Enterprise rs1:SECONDARY> use test1
switched to db test1
MongoDB Enterprise rs1:SECONDARY> show collections
student
teacher
MongoDB Enterprise rs1:SECONDARY> db.student.find({},{"_id":0})
{ "name" : "Zhaoliu", "age" : 25, "course" : "Chinese" }
{ "name" : "Zhangsan", "age" : 22, "course" : "Chinese" }
{ "name" : "Lisi", "age" : 23, "course" : "computer" }
{ "name" : "Liuneng", "age" : 26, "course" : "English" }
{ "name" : "Wangwu", "age" : 24, "course" : "Chinese" }
{ "name" : "Xieguangkun", "age" : 42, "course" : "Art" }

连接到27019,并查询状态,可以看到是arbiter:
MongoDB Enterprise rs1:SECONDARY> exit
bye
db2a:~ # mongo 192.168.37.1:27019
MongoDB Enterprise rs1:ARBITER> rs.isMaster()
{        "hosts" : [                "192.168.37.1:27017",                "192.168.37.1:27018"        ],        "arbiters" : [                "192.168.37.1:27019"        ],        "setName" : "rs1",        "setVersion" : 1,        "ismaster" : false,        "secondary" : false,        "primary" : "192.168.37.1:27017",        "arbiterOnly" : true,        "me" : "192.168.37.1:27019",..}

现在把27017的服务关闭,看一下27018会不会自动变为PRIMARY, 把session1里的mongodb服务结束使用Ctrl+c结束掉,再次查询session2的isMaster(),发现变为primary了:
MongoDB Enterprise rs1:ARBITER> exit
bye
db2a:~ # mongo 192.168.37.1:27018
MongoDB Enterprise rs1:PRIMARY> rs.isMaster()
{        "hosts" : [                "192.168.37.1:27017",                "192.168.37.1:27018"        ],        "arbiters" : [                "192.168.37.1:27019"        ],        "setName" : "rs1",        "setVersion" : 1,        "ismaster" : true,        "secondary" : false,        "primary" : "192.168.37.1:27018",        "me" : "192.168.37.1:27018",        "electionId" : ObjectId("7fffffff0000000000000002"),...}

查看复制集状态,可以看到27017的状态已经变为"stateStr" : "(not reachable/healthy)"
MongoDB Enterprise rs1:PRIMARY> rs.status()
{...        "members" : [                {                        "_id" : 0,                        "name" : "192.168.37.1:27017",                        "health" : 0,                        "state" : 8,                        "stateStr" : "(not reachable/healthy)",                        "uptime" : 0,...                },                {                        "_id" : 1,                        "name" : "192.168.37.1:27018",                        "health" : 1,                        "state" : 1,                        "stateStr" : "PRIMARY",                        "uptime" : 2475,...                },                {                        "_id" : 2,                        "name" : "192.168.37.1:27019",                        "health" : 1,                        "state" : 7,                        "stateStr" : "ARBITER",                        "uptime" : 2421,....                }        ],        "ok" : 1}

再次在session1里启动mongodb服务之后,查看复制状态,发现27017变为SECONDARY:
MongoDB Enterprise rs1:PRIMARY> rs.status()
{...        "members" : [                {                        "_id" : 0,                        "name" : "192.168.37.1:27017",                        "health" : 1,                        "state" : 2,                        "stateStr" : "SECONDARY",                        "uptime" : 29,...                },                {                        "_id" : 1,                        "name" : "192.168.37.1:27018",                        "health" : 1,                        "state" : 1,                        "stateStr" : "PRIMARY",                        "uptime" : 2702,...                },                {                        "_id" : 2,                        "name" : "192.168.37.1:27019",                        "health" : 1,                        "state" : 7,                        "stateStr" : "ARBITER",                        "uptime" : 2648,...                }        ],        "ok" : 1}

11.2 无仲裁节点

理论上不是需要仲裁节点的,这次只用了两个节点,db2a上一个primary,db2b上一个secondary:

db2a:~ # mkdir /data/repdb4
db2a:~ # mongod --dbpath=/data/repdb4 --port 27017 --replSet rs2
db2b:~ # mkdir /data/repdb1
db2b:~ # mongod --dbpath=/data/repdb1 --port 27017 --replSet rs2

db2a:
db2a:~ # mongo db2a:27017
> config = {_id:"rs2", members:[{_id:0,host:"db2a:27017"}, {_id:1,host:"db2b:27017"}]}
> rs.initiate(config)
rs2:PRIMARY> rs.status()
rs2:PRIMARY> rs.isMaster()
> use test1
> db.student.insert({name:'Zhaoliu',age:25,course:'Chinese'})
> db.student.insert({name:'Zhangsan',age:22,course:'Chinese'})

db2b:
db2b:~ # mongo db2b:27017
rs2:SECONDARY> rs.slaveOk()
rs2:SECONDARY> use test1
rs2:SECONDARY> db.student.find({},{"_id":0})
{ "name" : "Zhaoliu", "age" : 25, "course" : "Chinese" }
{ "name" : "Zhangsan", "age" : 22, "course" : "Chinese" }

但中断db2a的服务之后,db2b不会自动变成Primary,只有再次启动db2a服务之后,db2b才变成了primary。

再添加一个节点:
db2b上:
db2b:~ # mkdir /data/repdb2
db2b:~ # mongod --dbpath=/data/repdb2 --port 27018 --replSet rs2

在mongo shell里:
MongoDB Enterprise rs2:PRIMARY> rs.add('db2b:27018')
{ "ok" : 1 }
MongoDB Enterprise rs2:PRIMARY> rs.status()
{..        "members" : [                {                        "_id" : 0,                        "name" : "db2a:27017",                        "health" : 1,                        "state" : 2,                        "stateStr" : "SECONDARY",...                },                {                        "_id" : 1,                        "name" : "db2b:27017",                        "health" : 1,                        "state" : 1,                        "stateStr" : "PRIMARY",...                },                {                        "_id" : 2,                        "name" : "db2b:27018",                        "health" : 1,                        "state" : 2,                        "stateStr" : "SECONDARY",                        "uptime" : 60,...                }        ],        "ok" : 1}

这种情况下,如果将PRIMARY停掉(db2b:27017),那么剩下的两个节点会自动选出一个来作为PRIMARY

阅读全文
0 0
原创粉丝点击