MongoDB(五)-- 副本集(replica Set)

1、副本集介绍

搭建副本集是为了实现mongodb高可用。java

img

Mongodb(M)表示主节点,Mongodb(S)表示备节点,Mongodb(A)表示仲裁节点。主备节点存储数据,仲裁节点不存储数据。客户端同时链接主节点与备节点,不链接仲裁节点。node

仲裁节点是一种特殊的节点,它自己并不存储数据,主要的做用是决定哪个备节点在主节点挂掉以后提高为主节点,因此客户端不须要链接此节点。mongodb

在MongoDB副本集中,主节点负责处理客户端的读写请求,备份节点则负责映射主节点的数据。shell

备份节点的工做原理过程能够大体描述为,备份节点按期轮询主节点上的数据操做,而后对本身的数据副本进行这些操做,从而保证跟主节点的数据同步。至于主节点上的全部 数据库状态改变 的操做,都会存放在一张特定的系统表中。备份节点则是根据这些数据进行本身的数据更新。数据库

上面提到的 数据库状态改变 的操做,称为oplog(operation log,主节点操做记录)。oplog存储在local数据库的"oplog.rs"表中。副本集中备份节点异步的从主节点同步oplog,而后从新执行它记录的操做,以此达到了数据同步的做用。vim

Oplog注意点:bash

  • Oplog的大小是固定的,当集合被填满的时候,新的插入的文档会覆盖老的文档。
  • Oplog同步数据

初始化:这个过程发生在当副本集中建立一个新的数据库或其中某个节点刚从宕机中恢复,或者向副本集中添加新的成员的时候,默认的,副本集中的节点会从离它最近的节点复制oplog来同步数据,这个最近的节点能够是primary也能够是拥有最新oplog副本的secondary节点。服务器

2、搭建有仲裁节点的副本集

1.进入到/usr/java中,新建mongodbRepliSet文件夹,而后在 mongodbRepliSet 文件夹中新建 3个节点,先 mkdir node1。app

2.进入到 node1中,新建 data 和 log文件夹,即 mkdir data log,而后进入到 data中,mkdir db。异步

3.拷贝mongodb的配置文件到 node1中,

cp /usr/java/mongoNode/mongodb.conf /usr/java/mongodbRepliSet/node1/mongodb.conf

4.修改配置文件,vim mongodb.conf

dbpath=/usr/java/mongodbRepliSet/node1/data//db
logpath=/usr/java/mongodbRepliSet/node1/log/mongodb.log
logappend=true
fork=true
bind_ip=192.168.80.128
port=27017
replSet=JoeSet   # 3个节点的这个配置要同样,表示在一个副本集中
复制代码

5.拷贝 node1 整个文件夹,名为 node2 和 node3

cp -r node1 node2
cp -r node1 node3
复制代码

6.修改 node2 和 node3 文件夹中的mongodb.conf,修改 dbpath、logpath 和 port(2701八、27019)。

7.配置 临时的环境变量,export PATH=/usr/java/mongodb/bin:$PATH

8.查看临时的环境变量是否配置成功:echo $PATH

9.启动3个节点,分别进入 node1 node2和node3中,以配置文件方式启动:mongod --config mongodb.conf

10.客户端链接node1,mongo --host 192.168.80.128 --port 27017

11.上述配置 没有指定哪个是master、slave、仲裁节点,因此须要执行下 副本集的初始化,执行:

rs.initiate({"_id":"JoeSet",members:[{"_id":1,"host":"192.168.80.128:27017",priority:3},{"_id":2,"host":"192.168.80.128:27018", priority:9},{"_id":3,"host":"192.168.80.128:27019",arbiterOnly:true}]})。

  1. "_id": 副本集的名称
  2. "members": 副本集的服务器列表
  3. "_id": 服务器的惟一ID
  4. "host": 服务器主机
  5. "priority": 是优先级,默认为1,优先级0为被动节点,不能成为活跃节点。优先级不位0则按照有大到小选出活跃节点。
  6. "arbiterOnly": 仲裁节点,只参与投票,不接收数据,也不能成为活跃节点。

img

执行完 初始化命令后,会变为上图所示。

注意,可能出现的错误:

> rs.initiate({"_id":"JoeSet",members:[{"_id":1,"host":"192.168.80.128:27017",priority:3},{"_id":2,"host":"192.168.80.128:27018", priority:9},{"_id":4,"host":"192.168.80.128:27019",arbiterOnly:true}]})
{
    "ok" : 0,
    "errmsg" : "This node, 192.168.80.128:27019, with _id 4 is not electable under the new configuration version 1 for replica set JoeSet",
    "code" : 93
}
复制代码

错误缘由,是由于客户端链接了 27019,而27019 又是仲裁节点,因此出现了这个错误。解决方法,客户端链接 其余节点,不链接 设置为仲裁的节点。

12.执行:rs.status(),查看状态。

JoeSet:OTHER> rs.status()
{
    "set" : "JoeSet",
    "date" : ISODate("2017-07-26T22:09:44.940Z"),
    "myState" : 2,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.80.128:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",        # SECONDARY 表示从节点
            "uptime" : 38,
            "optime" : Timestamp(1501106974, 1),
            "optimeDate" : ISODate("2017-07-26T22:09:34Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.80.128:27018",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",        # PRIMARY 表示主节点
            "uptime" : 10,
            "optime" : Timestamp(1501106974, 1),
            "optimeDate" : ISODate("2017-07-26T22:09:34Z"),
            "lastHeartbeat" : ISODate("2017-07-26T22:09:44.560Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-26T22:09:44.579Z"),
            "pingMs" : 1,
            "electionTime" : Timestamp(1501106981, 1),
            "electionDate" : ISODate("2017-07-26T22:09:41Z"),
            "configVersion" : 1
        },
        {
            "_id" : 3,
            "name" : "192.168.80.128:27019",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",        # ARBITER 表示仲裁节点
            "uptime" : 10,
            "lastHeartbeat" : ISODate("2017-07-26T22:09:44.559Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-26T22:09:44.585Z"),
            "pingMs" : 0,
            "configVersion" : 1
        }
    ],
    "ok" : 1
}
复制代码

13.为了验证副本集搭建成功,咱们 在node2(主节点) 中插入几条数据,而后在 node1 (从节点)中查看,由于 上面配置了 node2 为主节点, node1为从节点,node3为仲裁节点。

  1. 在node2中:
JoeSet:PRIMARY> show dbs
local  0.078GB
JoeSet:PRIMARY> use testdb
switched to db testdb
JoeSet:PRIMARY> db.createCollection("testCon")
{ "ok" : 1 }
JoeSet:PRIMARY> show collections
system.indexes
testCon
JoeSet:PRIMARY> for(var i=0; i<3;i++) db.testCon.insert({name:"Joe",index:i})
WriteResult({ "nInserted" : 1 })
JoeSet:PRIMARY> db.testCon.find()
{ "_id" : ObjectId("597a553d7db09ad9f77f1353"), "name" : "Joe", "index" : 0 }
{ "_id" : ObjectId("597a553d7db09ad9f77f1354"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("597a553d7db09ad9f77f1355"), "name" : "Joe", "index" : 2 }
复制代码
  1. 在node1中:
JoeSet:SECONDARY> show dbs
2017-07-27T14:06:14.672-0700 E QUERY    Error: listDatabases failed:{ "note" : "from execCommand", "ok" : 0, "errmsg" : "not master" }
    at Error (<anonymous>)
    at Mongo.getDBs (src/mongo/shell/mongo.js:47:15)
    at shellHelper.show (src/mongo/shell/utils.js:630:33)
    at shellHelper (src/mongo/shell/utils.js:524:36)
    at (shellhelp2):1:1 at src/mongo/shell/mongo.js:47
JoeSet:SECONDARY> rs.slaveOk()
JoeSet:SECONDARY> show dbs
local   0.078GB
testdb  0.078GB
JoeSet:SECONDARY> use testdb
switched to db testdb
JoeSet:SECONDARY> show collections
system.indexes
testCon
JoeSet:SECONDARY> db.testCon.find()
{ "_id" : ObjectId("597a553d7db09ad9f77f1353"), "name" : "Joe", "index" : 0 }
{ "_id" : ObjectId("597a553d7db09ad9f77f1354"), "name" : "Joe", "index" : 1 }
{ "_id" : ObjectId("597a553d7db09ad9f77f1355"), "name" : "Joe", "index" : 2 }
复制代码

在node1(从节点)中 能够查看到 node2(主节点)插入的数据,说明 副本集搭建成功。

注意:从节点中是不容许执行写操做的。

3、模拟主节点故障及恢复

  1. 模拟node2(主节点)挂了,kiil 掉node2(主节点)的进程

  2. 客户端链接 node1(从节点):mongo --host 192.168.80.128 --port 27017

JoeSet:PRIMARY> rs.status()
{
    "set" : "JoeSet",
    "date" : ISODate("2017-07-27T21:20:45.629Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.80.128:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 4209,
            "optime" : Timestamp(1501189437, 10),
            "optimeDate" : ISODate("2017-07-27T21:03:57Z"),
            "electionTime" : Timestamp(1501190187, 1),
            "electionDate" : ISODate("2017-07-27T21:16:27Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.80.128:27018",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",
            "uptime" : 0,
            "optime" : Timestamp(0, 0),
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2017-07-27T21:20:43.773Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-27T21:16:24.292Z"),
            "pingMs" : 1,
            "lastHeartbeatMessage" : "Failed attempt to connect to 192.168.80.128:27018; couldn't connect to server 192.168.80.128:27018 (192.168.80.128), connection attempt failed",
            "configVersion" : -1
        },
        {
            "_id" : 3,
            "name" : "192.168.80.128:27019",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 4191,
            "lastHeartbeat" : ISODate("2017-07-27T21:20:45.475Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-27T21:20:44.876Z"),
            "pingMs" : 0,
            "configVersion" : 1
        }
    ],
    "ok" : 1
} 
复制代码

发现原来的从节点(node1)变为了 主节点,而原来的主节点显示的状态是 不可达、不健康的。这对于整个副本集的使用是没有影响的。

  1. 从新启动node2,即 原来的主节点。若是启动node2失败,就删除掉 db文件夹下的mongod.lock文件。

  2. 客户端链接node1,mongo --host 192.168.80.128 --port 27017,发现node2 从新启动后,node1 又变为 从节点,而 node2又变为 原来的主节点,这是由于 仲裁节点 和 设置的优先级的缘由。

JoeSet:SECONDARY> rs.status()
{
    "set" : "JoeSet",
    "date" : ISODate("2017-07-27T21:31:06.197Z"),
    "myState" : 2,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.80.128:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 4830,
            "optime" : Timestamp(1501189437, 10),
            "optimeDate" : ISODate("2017-07-27T21:03:57Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.80.128:27018",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 87,
            "optime" : Timestamp(1501189437, 10),
            "optimeDate" : ISODate("2017-07-27T21:03:57Z"),
            "lastHeartbeat" : ISODate("2017-07-27T21:31:05.209Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-27T21:31:05.506Z"),
            "pingMs" : 0,
            "electionTime" : Timestamp(1501190981, 1),
            "electionDate" : ISODate("2017-07-27T21:29:41Z"),
            "configVersion" : 1
        },
        {
            "_id" : 3,
            "name" : "192.168.80.128:27019",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 4811,
            "lastHeartbeat" : ISODate("2017-07-27T21:31:06.085Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-27T21:31:05.633Z"),
            "pingMs" : 0,
            "configVersion" : 1
        }
    ],
    "ok" : 1
}
复制代码

当仲裁节点挂掉后,达不到高可用了,即当 主节点挂了后,从节点在没有仲裁节点的状况下,不会切换为 主节点了。因此,推荐使用没有仲裁节点的副本集,以下。

4、搭建没有仲裁节点的副本集,推荐使用

1.复制node1 为 node六、node七、node8:

cp  -r  node1   node6
cp  -r  node1   node7
cp  -r  node1   node8
复制代码

2.清空node六、node七、node8的db文件夹 和 log 文件夹

rm -rf data/db/*
rm -rf log/*
复制代码

3.修改node六、node七、node8 的datapath、logpath和port、replSet

node6 的port 为 27021,node7 的port 为 27022,node8 的port 为 27023,replSet 为 XbqSet

4.启动三个节点,分别进入 对应的node文件夹中:

mongod --config mongodb.conf

5.链接node6节点(27021):mongo --host 192.168.80.128 --port 27021

6.执行初始化命令:rs.initiate({"_id":"XbqSet",members:[{"_id":1,"host":"192.168.80.128:27021"},{"_id":2,"host":"192.168.80.128:27022"},{"_id":3,"host":"192.168.80.128:27023" }]})

7.查看状态,发现有一个主节点,两个从节点。

XbqSet:PRIMARY> rs.status()
{
    "set" : "XbqSet",
    "date" : ISODate("2017-07-27T22:02:28.188Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.80.128:27021",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 483,
            "optime" : Timestamp(1501192872, 1),
            "optimeDate" : ISODate("2017-07-27T22:01:12Z"),
            "electionTime" : Timestamp(1501192876, 1),
            "electionDate" : ISODate("2017-07-27T22:01:16Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.80.128:27022",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 75,
            "optime" : Timestamp(1501192872, 1),
            "optimeDate" : ISODate("2017-07-27T22:01:12Z"),
            "lastHeartbeat" : ISODate("2017-07-27T22:02:26.973Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-27T22:02:26.973Z"),
            "pingMs" : 0,
            "configVersion" : 1
        },
        {
            "_id" : 3,
            "name" : "192.168.80.128:27023",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 75,
            "optime" : Timestamp(1501192872, 1),
            "optimeDate" : ISODate("2017-07-27T22:01:12Z"),
            "lastHeartbeat" : ISODate("2017-07-27T22:02:26.973Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-27T22:02:26.973Z"),
            "pingMs" : 0,
            "configVersion" : 1
        }
    ],
    "ok" : 1
}
复制代码

8.kill 掉node6,即端口 27021

9.而后链接到node7上:mongo --host 192.168.80.128 --port 27022,查看状态,发现node7为从节点,node8为主节点了,原来的node6 不可达、不健康的。

XbqSet:SECONDARY> rs.status()
{
    "set" : "XbqSet",
    "date" : ISODate("2017-07-27T22:08:24.894Z"),
    "myState" : 2,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.80.128:27021",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",
            "uptime" : 0,
            "optime" : Timestamp(0, 0),
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2017-07-27T22:08:23.748Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-27T22:07:43.464Z"),
            "pingMs" : 0,
            "lastHeartbeatMessage" : "Failed attempt to connect to 192.168.80.128:27021; couldn't connect to server 192.168.80.128:27021 (192.168.80.128), connection attempt failed",
            "configVersion" : -1
        },
        {
            "_id" : 2,
            "name" : "192.168.80.128:27022",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 833,
            "optime" : Timestamp(1501192872, 1),
            "optimeDate" : ISODate("2017-07-27T22:01:12Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 3,
            "name" : "192.168.80.128:27023",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 431,
            "optime" : Timestamp(1501192872, 1),
            "optimeDate" : ISODate("2017-07-27T22:01:12Z"),
            "lastHeartbeat" : ISODate("2017-07-27T22:08:23.409Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-27T22:08:23.521Z"),
            "pingMs" : 0,
            "electionTime" : Timestamp(1501193266, 1),
            "electionDate" : ISODate("2017-07-27T22:07:46Z"),
            "configVersion" : 1
        }
    ],
    "ok" : 1
}
复制代码

10.从新启动node6:mongod --config mongodb.conf

11.继续链接node7:mongo --host 192.168.80.128 --port 27022,查看状态,原来的node6 变为了 从节点。

XbqSet:SECONDARY> rs.status()
{
    "set" : "XbqSet",
    "date" : ISODate("2017-07-27T22:12:41.275Z"),
    "myState" : 2,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.80.128:27021",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 54,
            "optime" : Timestamp(1501192872, 1),
            "optimeDate" : ISODate("2017-07-27T22:01:12Z"),
            "lastHeartbeat" : ISODate("2017-07-27T22:12:40.382Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-27T22:12:41.213Z"),
            "pingMs" : 0,
            "configVersion" : 1
        },
        {
            "_id" : 2,
            "name" : "192.168.80.128:27022",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 1090,
            "optime" : Timestamp(1501192872, 1),
            "optimeDate" : ISODate("2017-07-27T22:01:12Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 3,
            "name" : "192.168.80.128:27023",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 688,
            "optime" : Timestamp(1501192872, 1),
            "optimeDate" : ISODate("2017-07-27T22:01:12Z"),
            "lastHeartbeat" : ISODate("2017-07-27T22:12:39.673Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-27T22:12:39.820Z"),
            "pingMs" : 0,
            "electionTime" : Timestamp(1501193266, 1),
            "electionDate" : ISODate("2017-07-27T22:07:46Z"),
            "configVersion" : 1
        }
    ],
    "ok" : 1
}
复制代码

5、增长、删除节点

1.cp -r node8 node10,修改node10的datapath、logpath的port,并将port 改成 27024,启动 node10。

2.进入到主节点中,mongo --host 192.168.80.128 --port 27023,必定要在主节点中进行操做。

3.增长节点:rs.add(“192.168.80.128:27024”)

XbqSet:PRIMARY> rs.add("192.168.80.128:27024")
{ "ok" : 1 }
复制代码

4.查看状态,rs.status(),发现新增长的节点 为从节点,即 SECONDARY。

XbqSet:PRIMARY> rs.status()
{
    "set" : "XbqSet",
    "date" : ISODate("2017-07-29T14:31:44.872Z"),
    "myState" : 1,
    "members" : [
        {
            "_id" : 1,
            "name" : "192.168.80.128:27021",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 630,
            "optime" : Timestamp(1501338697, 1),
            "optimeDate" : ISODate("2017-07-29T14:31:37Z"),
            "lastHeartbeat" : ISODate("2017-07-29T14:31:43.555Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-29T14:31:43.457Z"),
            "pingMs" : 0,
            "configVersion" : 2
        },
        {
            "_id" : 2,
            "name" : "192.168.80.128:27022",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 1854,
            "optime" : Timestamp(1501338697, 1),
            "optimeDate" : ISODate("2017-07-29T14:31:37Z"),
            "lastHeartbeat" : ISODate("2017-07-29T14:31:43.555Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-29T14:31:43.186Z"),
            "pingMs" : 0,
            "configVersion" : 2
        },
        {
            "_id" : 3,
            "name" : "192.168.80.128:27023",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 1953,
            "optime" : Timestamp(1501338697, 1),
            "optimeDate" : ISODate("2017-07-29T14:31:37Z"),
            "electionTime" : Timestamp(1501338022, 1),
            "electionDate" : ISODate("2017-07-29T14:20:22Z"),
            "configVersion" : 2,
            "self" : true
        },
        {
            "_id" : 4,
            "name" : "192.168.80.128:27024",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 7,
            "optime" : Timestamp(1501338697, 1),
            "optimeDate" : ISODate("2017-07-29T14:31:37Z"),
            "lastHeartbeat" : ISODate("2017-07-29T14:31:43.566Z"),
            "lastHeartbeatRecv" : ISODate("2017-07-29T14:31:43.579Z"),
            "pingMs" : 6,
            "configVersion" : 2
        }
    ],
    "ok" : 1
}
复制代码

5.删除节点:rs.remove(hostportstr)

XbqSet:PRIMARY> rs.remove("192.168.80.128:27024")
{ "ok" : 1 }
复制代码

查看状态,发现 端口为27024的节点 没有了。


欢迎关注个人公众号,第一时间接收最新文章~ 搜索公众号: 码咖 或者 扫描下方二维码:

img
相关文章
相关标签/搜索