环境

在windows一台机器上部署3个副本集(replica set),端口分别是:27020, 27021, 27022,在e://mongo//replset下新建三个目录,分别是:27020,27021,27023。

创建副本集

打开命令行工具执行:

mongod --port 27020 --replSet rs0 --dbpath e://mongo/replset//27020

初始化副本集

打开命令行工具,连接到先前的服务:mongo --port 27020

初始化只有一个成员的副本集,后面可以新增(也可以初始化多个)。

rs.initiate({_id: "rs0", members:[{_id: 0, host: "127.0.0.1:27020"}]})

rs.conf()查看副本集配置, rs.status()查看副本集状态

新增副本集

打开新的命令行工具,开启新的服务,这里开启两个(可以开启多个)

mongod --port 27021 --replSet rs0 --dbpath e://mongo/replset//27021
mongod --port 27022 --replSet rs0 --dbpath e://mongo/replset//27022

连接到primary服务,也就是之前的27020端口,执行:

rs.add("127.0.0.1:27021")
rs.add("127.0.0.1:27022")

rs.status()可以看到如下信息:

{
        "set" : "rs0",
        "date" : ISODate("2017-12-03T05:49:38.949Z"),
        "myState" : 1,
        "term" : NumberLong(1),
        "heartbeatIntervalMillis" : NumberLong(2000),
        "optimes" : {
                "lastCommittedOpTime" : {
                        "ts" : Timestamp(1512280173, 1),
                        "t" : NumberLong(1)
                },
                "appliedOpTime" : {
                        "ts" : Timestamp(1512280173, 1),
                        "t" : NumberLong(1)
                },
                "durableOpTime" : {
                        "ts" : Timestamp(1512280173, 1),
                        "t" : NumberLong(1)
                }
        },
        "members" : [
                {
                        "_id" : 0,
                        "name" : "127.0.0.1:27020",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 888,
                        "optime" : {
                                "ts" : Timestamp(1512280173, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2017-12-03T05:49:33Z"),
                        "electionTime" : Timestamp(1512279507, 1),
                        "electionDate" : ISODate("2017-12-03T05:38:27Z"),
                        "configVersion" : 3,
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "127.0.0.1:27021",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 178,
                        "optime" : {
                                "ts" : Timestamp(1512280173, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1512280173, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2017-12-03T05:49:33Z"),
                        "optimeDurableDate" : ISODate("2017-12-03T05:49:33Z"),
                        "lastHeartbeat" : ISODate("2017-12-03T05:49:37.825Z"),
                        "lastHeartbeatRecv" : ISODate("2017-12-03T05:49:38.841Z"),
                        "pingMs" : NumberLong(0),
                        "configVersion" : 3
                },
                {
                        "_id" : 2,
                        "name" : "127.0.0.1:27022",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 5,
                        "optime" : {
                                "ts" : Timestamp(1512280173, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1512280173, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2017-12-03T05:49:33Z"),
                        "optimeDurableDate" : ISODate("2017-12-03T05:49:33Z"),
                        "lastHeartbeat" : ISODate("2017-12-03T05:49:37.825Z"),
                        "lastHeartbeatRecv" : ISODate("2017-12-03T05:49:35.213Z"),
                        "pingMs" : NumberLong(0),
                        "configVersion" : 3
                }
        ],
        "ok" : 1
}

副本集有三个成员,其中_id为0的是primary

nodejs测试

首先安装:npm install mongodb --save
代码如下:

var MongoClient = require('mongodb').MongoClient;
var DB_CONN_STR = 'mongodb://localhost:27020,localhost:27021,localhost:27022/test?replicaSet=rs0';

var insertData = function(db, callback) {  
    //连接到表 site
    var collection = db.collection('test');
    //插入数据
    var data = [{"name":"hello","url":"3inns.cn"}];
    collection.insert(data, function(err, result) { 
        if(err)
        {
            console.log('Error:'+ err);
            return;
        }     
        callback(result);
    });
}

MongoClient.connect(DB_CONN_STR, function(err, db) {
    console.log("连接成功!");
    let i = 0;
    setInterval(function() {
        console.log(i++);
        insertData(db, function(result) {
            console.log(result);
            // db.close();
        });
    }, 5000);
});

注意副本集的url配置:mongodb://localhost:27020,localhost:27021,localhost:27022/test?replicaSet=rs0

每五秒钟向test数据库的test集合插入一条数据。当我关掉primary主服务后(目前是27020端口)nodejs程序写入数据会失败个10几秒钟,过一会就正常了,而且之前写入失败的数据也会自动补上的。

此时再用rs.status()查看发现27022变成了primary,27020not reachable/healthy

如果再关掉27022服务,发现nodejs程序写数据一直是失败的,看来副本集中至少要有两个成员存活才能完成自动切换到primary

启动27022服务,写入就正常了,之前写入失败的操作也会重新补上。
再启动27020就完整了,查看各个服务中数据库的数据是否一致。

副本集个数

需要满足“大多数”的概念,有两种方式:

  • 奇数个,至少要部署3个
  • 偶数个,需要再部署一个仲裁者

此个数是配置的个数,并不是运行中的成员个数。所以当某台服务挂了副本集的个数还是不变的。