mongodb 副本集(Replica Set)搭建

本节介绍mongodb副本集的原理及搭建方法,MongoDB shell version: 3.2.1,ubuntu@14.04

在读这篇文章之前,可能你需要阅读上一篇文章:mognodb 副本集复制概念理解
mongodb副本集是有故障恢复功能的主从集群,由一个primary节点和一个或多个secondary节点组成。
同步过程: Primary节点写入数据,Secondary通过读取Primary的oplog得到复制信息,开始复制数据并且将复制信息写入到自己的oplog。如果某个操作失败,则备份节点停止从当前数据源复制数据。如果某个备份节点由于某些原因挂掉了,当重新启动后,就会自动从oplog的最后一个操作开始同步,同步完成后,将信息写入自己的oplog,由于复制操作是先复制数据,复制完成后再写入oplog,有可能相同的操作会同步两份,不过MongoDB在设计之初就考虑到这个问题,将oplog的同一个操作执行多次,与执行一次的效果是一样的。
简单的说就是:当Primary节点完成数据操作后,Secondary会做出一系列的动作保证数据的同步

  • 1、检查自己local库的oplog.rs集合,找出最近的时间戳。
  • 2、检查Primary节点local库oplog.rs集合,找出大于此时间戳的记录。
  • 3、将找到的记录插入到自己的oplog.rs集合中,并执行这些操作。

副本集的同步和主从同步一样,都是异步同步的过程,不同的是副本集有个自动故障转移的功能。其原理是:slave端从primary端获取日志,然后在自己身上完全顺序的执行日志所记录的各种操作(该日志是不记录查询操作的),这个日志就是local数据 库中的oplog.rs表,默认在64位机器上这个表是比较大的,占磁盘大小的5%,oplog.rs的大小可以在启动参数中设 定:–oplogSize 1000,单位是M。

注意:在副本集的环境中,要是所有的Secondary都宕机了,只剩下Primary。最后Primary会变成Secondary,不能提供服务。

开发环境搭建

准备服务器

#由于是开发用,我们准备三个端口
27017
27018
27019

停止所有mongod

sudo service mongod stop

创建数据库目录

$ sudo mkdir -p /data/mongodb/rs0-0 /data/mongodb/rs0-1 /data/mongodb/rs0-2

这里创建了rs0-0,rs0-1,rs0-2目录用来存储数据库文件。

创建mongod实例

#第一个成员
sudo mongod --port 27017 --dbpath /data/mongodb/rs0-0 --replSet rs0 --smallfiles --oplogSize 128 &   #--oplogsize 单位M.
#第二个成员
sudo mongod --port 27018 --dbpath /data/mongodb/rs0-1 --replSet rs0 --smallfiles --oplogSize 128 &
#第三个成员
sudo mongod --port 27019 --dbpath /data/mongodb/rs0-2 --replSet rs0 --smallfiles --oplogSize 128 &

这里创建了三个所属副本集名为rs0的节点,每个节点的数据目录通过–dbpath指定,端口通过–port指定,–smallfiles–oplogSize设置减少每个mongod实例使用的磁盘空间。这是理想的开发环境,防止机器过载。

连接数据库

$ mongo --port 27017

这里本地环境,需要指定端口号。

初始化副本集

#编写配置文件格式
$ rsconf = {
           _id: "rs0",
           members: [
                 {
                       _id: 0,
                       host: "<hostname>:27017"
                 }
          ]
}
#下面是我的配置
$ rsconf = {
            "_id" : "rs0",
            "members" : [
                    {
                            "_id" : 0,
                            "host" : "192.168.199.164:27017"
                    },
                    {
                            "_id" : 1,
                            "host" : "192.168.199.164:27018"
                    },
                    {
                            "_id" : 2,
                            "host" : "192.168.199.164:27019"
                    }
            ]
}

替换hostname:27017为你的主机名,之后通过rsconf去初始化:

$ rs.initiate( rsconf )

查看当前配置

$ rs.conf()
{
    "_id" : "rs0",
    "version" : 1,
    "protocolVersion" : NumberLong(1),
    "members" : [
        {
            "_id" : 0,
            "host" : "192.168.199.164:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {

            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 1,
            "host" : "192.168.199.164:27018",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {

            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 2,
            "host" : "192.168.199.164:27019",
            "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
        }
    }
}

在mongodb会将配置里的节点自动添加到副本集rs0中,后面会讲到副本集的维护。

查看副本集状态

$ rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2016-05-15T06:34:22.369Z"),
    "myState" : 1,
    "term" : NumberLong(1),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "192.168.199.164:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 1142,
            "optime" : {
                "ts" : Timestamp(1463293650, 2),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2016-05-15T06:27:30Z"),
            "electionTime" : Timestamp(1463293650, 1),
            "electionDate" : ISODate("2016-05-15T06:27:30Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "192.168.199.164:27018",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 422,
            "optime" : {
                "ts" : Timestamp(1463293650, 2),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2016-05-15T06:27:30Z"),
            "lastHeartbeat" : ISODate("2016-05-15T06:34:20.407Z"),
            "lastHeartbeatRecv" : ISODate("2016-05-15T06:34:21.504Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "192.168.199.164:27017",
            "configVersion" : 1
        },
        {
            "_id" : 2,
            "name" : "192.168.199.164:27019",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 422,
            "optime" : {
                "ts" : Timestamp(1463293650, 2),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2016-05-15T06:27:30Z"),
            "lastHeartbeat" : ISODate("2016-05-15T06:34:20.407Z"),
            "lastHeartbeatRecv" : ISODate("2016-05-15T06:34:21.504Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "192.168.199.164:27017",
            "configVersion" : 1
        }
    ],
    "ok" : 1
}

到这里,整个副本集已经搭建成功了。

功能测试

复制功能

在Primary(192.168.199.164:27017)上插入数据

#我们连接到主节点Primary
$ mongo --port 27017
#查看数据库,只有一个local
$ show dbs

local  0.000GB
#新建数据库
$ use testdb
#向数据库插入数据
$ db.test.insert({"name":"test data","value":1111})
#查看是否插入成功
$ db.test.find()

{ "_id" : ObjectId("57382a2641cff36fe7f3fc30"), "name" : "test data", "value" : 1111 }

我们断开连接,连接到任一从节点Secondary(192.168.199.164:27018)

$ mongo --port 27018
#切换数据库
$ use testdb
#查看数据,这里不能直接用 db.test.find() 查看。mongodb默认是从主节点读写数据的,副本节点上不允许读,需要设置副本节点可以读。
$ db.getMongo().setSlaveOk()
#查看数据
$ db.test.find()

{ "_id" : ObjectId("57382a2641cff36fe7f3fc30"), "name" : "test data", "value" : 1111 }

可以看到数据已经同步过来了,测试OK。

自动故障转移

我们先查看各个节点状态:

$ rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2016-05-15T08:03:51.757Z"),
    "myState" : 2,
    "term" : NumberLong(1),
    "syncingTo" : "192.168.199.164:27017",
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "192.168.199.164:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 5790,
            "optime" : {
                "ts" : Timestamp(1463298598, 2),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2016-05-15T07:49:58Z"),
            "lastHeartbeat" : ISODate("2016-05-15T08:03:50.886Z"),
            "lastHeartbeatRecv" : ISODate("2016-05-15T08:03:49.938Z"),
            "pingMs" : NumberLong(0),
            "electionTime" : Timestamp(0, 0),
            "electionDate" : ISODate("1970-01-01T00:00:00Z"),
            "configVersion" : 1
        },
        {
            "_id" : 1,
            "name" : "192.168.199.164:27018",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 6370,
            "optime" : {
                "ts" : Timestamp(1463298598, 2),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2016-05-15T07:49:58Z"),
            "syncingTo" : "192.168.199.164:27017",
            "configVersion" : 1,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.199.164:27019",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 5790,
            "optime" : {
                "ts" : Timestamp(1463298598, 2),
                "t" : NumberLong(1)
            },
            "optimeDate" : ISODate("2016-05-15T07:49:58Z"),
            "lastHeartbeat" : ISODate("2016-05-15T08:03:50.886Z"),
            "lastHeartbeatRecv" : ISODate("2016-05-15T08:03:51.021Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "192.168.199.164:27017",
            "configVersion" : 1
        }
    ],
    "ok" : 1
}

关掉主节点Primary:

#连接到主节点primary
$ mongo --port 27017
#切换到 admin 数据库(关掉数据库命令只能在 admin 数据库)
$ use admin
#关掉
$ db.shutdownServer()

我们再进入任一节点:

$ mongo --port 27018
#查看副本集状态
$ rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2016-05-15T08:16:12.253Z"),
    "myState" : 1,
    "term" : NumberLong(2),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "192.168.199.164:27017",
            "health" : 0,
            "state" : 8,
            "stateStr" : "(not reachable/healthy)",                        #这里变为不可达状态
            "uptime" : 0,
            "optime" : {
                "ts" : Timestamp(0, 0),
                "t" : NumberLong(-1)
            },
            "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
            "lastHeartbeat" : ISODate("2016-05-15T08:16:11.064Z"),
            "lastHeartbeatRecv" : ISODate("2016-05-15T08:09:36.193Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "Connection refused",
            "configVersion" : -1
        },
        {
            "_id" : 1,
            "name" : "192.168.199.164:27018",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",                                        #成为主节点
            "uptime" : 7111,
            "optime" : {
                "ts" : Timestamp(1463299787, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2016-05-15T08:09:47Z"),
            "electionTime" : Timestamp(1463299786, 1),
            "electionDate" : ISODate("2016-05-15T08:09:46Z"),
            "configVersion" : 1,
            "self" : true
        },
        {
            ......
        }
    ],
    "ok" : 1
}

可以看到192.168.199.164:27017这台已经是不可达的状态了。
192.168.199.164:27018被选举成为新的主节点Primary。自动故障转移测试成功。

我们再次做数据同步测试:

#连接主节点
$ mongo --port 27018
$ use testdb
#向新的主节点 :27018 插入数据
$ db.test.insert({"name" : "test data2", "value" : 2222})
#查看数据
$ db.test.find()

{ "_id" : ObjectId("57382a2641cff36fe7f3fc30"), "name" : "test data", "value" : 1111 }
{ "_id" : ObjectId("573831bc15f8a9f0fd3e51ee"), "name" : "test data2", "value" : 2222 }

下面我们再次启动之前停掉的主节点 27017。

#启动之前停掉的 27017
$ sudo mongod --port 27017 --dbpath /data/mongodb/rs0-0 --replSet rs0 --smallfiles --oplogSize 128 &
#连接该节点
$ mongo --port 27017
#切换数据库
$ use testdb
#设置可读
$ db.getMongo().setSlaveOk()
#查看数据
$ db.test.find()

{ "_id" : ObjectId("57382a2641cff36fe7f3fc30"), "name" : "test data", "value" : 1111 }
{ "_id" : ObjectId("573831bc15f8a9f0fd3e51ee"), "name" : "test data2", "value" : 2222 }

可以看到数据也已经同步过来,但是 27017 也已经成为从节点Secondary了。
注意:所有的Secondary都宕机、或则副本集中只剩下一个节点,则该节点只能为Secondary节点,也就意味着整个集群智能进行读操作而不能进行写操作,当其他的恢复时,之前的primary节点仍然是primary节点。官方推荐的最小的副本集也应该具备一个primary节点和两个secondary节点。两个节点的副本集不具备真正的故障转移能力。

维护

增删节点

添加一个节点

#创建目录
sudo makedir /data/rs0-3
#启动一个实例
$ sudo mongod --port 27020 --dbpath /data/mongodb/rs0-3 --replSet rs0 --smallfiles --oplogSize 128 &
#进入主节点Primary
$ mongo --port 27018
#添加到副本集
$ rs.add("192.168.199.164:27020")
#查看副本集状态
$ rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2016-05-15T09:06:32.461Z"),
    "myState" : 1,
    "term" : NumberLong(2),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            ......
        },
        {
            "_id" : 1,
            "name" : "192.168.199.164:27018",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 10131,
            "optime" : {
                "ts" : Timestamp(1463303179, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2016-05-15T09:06:19Z"),
            "electionTime" : Timestamp(1463299786, 1),
            "electionDate" : ISODate("2016-05-15T08:09:46Z"),
            "configVersion" : 2,
            "self" : true
        },
        {
            ......
        },
        {
            "_id" : 3,
            "name" : "192.168.199.164:27020",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 12,
            "optime" : {
                "ts" : Timestamp(1463303179, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2016-05-15T09:06:19Z"),
            "lastHeartbeat" : ISODate("2016-05-15T09:06:31.514Z"),
            "lastHeartbeatRecv" : ISODate("2016-05-15T09:06:27.758Z"),
            "pingMs" : NumberLong(0),
            "configVersion" : 2
        }
    ],
    "ok" : 1
}

可以看到已经添加成功。
删除一个节点

#进入需要删除的节点,切换到admin数据库,执行db.shutdownServer()关掉。
#进入主节点Primary,删除节点
$ rs.remove("192.168.199.164:27020")
#查看副本集状态
$ rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2016-05-15T09:14:04.615Z"),
    "myState" : 1,
    "term" : NumberLong(2),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            ......
        },
        {
            "_id" : 1,
            "name" : "192.168.199.164:27018",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 10583,
            "optime" : {
                "ts" : Timestamp(1463303631, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2016-05-15T09:13:51Z"),
            "electionTime" : Timestamp(1463299786, 1),
            "electionDate" : ISODate("2016-05-15T08:09:46Z"),
            "configVersion" : 3,
            "self" : true
        },
        {
            "_id" : 2,
            "name" : "192.168.199.164:27019",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 10003,
            "optime" : {
                "ts" : Timestamp(1463303631, 1),
                "t" : NumberLong(2)
            },
            "optimeDate" : ISODate("2016-05-15T09:13:51Z"),
            "lastHeartbeat" : ISODate("2016-05-15T09:14:03.065Z"),
            "lastHeartbeatRecv" : ISODate("2016-05-15T09:14:03.069Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "192.168.199.164:27018",
            "configVersion" : 3
        }
    ],
    "ok" : 1
}
#删除文件
$ sudo rm -rf rs0-3
#-------------------------------------------------------------------
#也可以通过副本集配置对象修改。
#进入需要删除的节点,切换到admin数据库,执行db.shutdownServer()关掉。
#进入主节点Primary,查看配置文档,找到需要删除的节点
$ mongo --port 27018
$ rs.conf()
{
    "_id" : "rs0",
    "version" : 1,
    "protocolVersion" : NumberLong(1),
    "members" : [
        {
            "_id" : 0,
            "host" : "192.168.199.164:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {

            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 1,
            "host" : "192.168.199.164:27018",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {

            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 2,
            "host" : "192.168.199.164:27019",
            "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
        }
    }
}
#获取到配置对象,删除,重新配置副本集
$ cfg = rs.conf()
$ cfg.members.splice(2,1)    #删除27019
$ rs.reconfig(cfg)

替换副本集节点

如果你需要更改一个节点的主机名并且不改变配置:

cfg = rs.conf()
cfg.members[0].host = "192.168.199.164:27021"
rs.reconfig(cfg)

改变Oplog大小

oplog存在内部限制集合,所以在正常操作过程中你不能修改它的大小。官网参考:oplog

配置副本集tag

标记让你定制写关心和读偏好一个复制集,在rs.conf()中配置,members[n].tags。tags

改变副本集主机名

一般,主机名是不会变化的。但在特殊情况你可能会改变它,请参考:hostname

本地数据库 local

每个mongod实例都有自己的本地数据库,用于存储数据复制过程,和其他特定数据。本地数据库是无形的复制:集合在本地数据库复制。请参考:local database

配置从节点Secondary同步对象

从节点的数据一般是从主节点进行复制,然而,在默认情况下,从节点可能基于各个节点ping的时间将同步目标改为其它从节点。对于一些部署,实现自定义的同步目标选择逻辑有可能会更有效。对于mongodb,你可以暂时覆盖默认的同步目标,手动配置第二个同步目标,拉去oplog。请参考:sync target

应用

手动切换主节点Primary

将指定的节点的优先级priority加到最大,即可成为主节点:

#切换到主节点Primary,查看配置
$ rs.conf()
{
    "_id" : "rs0",
    "version" : 3,        #每修改一次集群的配置,副本集的version都会+1
    "protocolVersion" : NumberLong(1),
    "members" : [
        {
            "_id" : 0,
            "host" : "192.168.199.164:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {

            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 1,
            "host" : "192.168.199.164:27018",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {

            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 2,
            "host" : "192.168.199.164:27019",
            "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
        }
    }
}
#修改优先级,让 27017 成为主节点
$ conf = rs.conf()
...
#修改优先级
$ conf.members[0].priority = 2
#重新加载配置文件,强制进行一次选举,期间所有节点均为从节点Secondary
$ rs.reconfig(conf)
#查看副本集状态
$ rs.status()
{
    "set" : "rs0",
    "date" : ISODate("2016-05-15T09:32:06.298Z"),
    "myState" : 2,
    "term" : NumberLong(3),
    "syncingTo" : "192.168.199.164:27019",
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "192.168.199.164:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 2874,
            "optime" : {
                "ts" : Timestamp(1463304708, 2),
                "t" : NumberLong(3)
            },
            "optimeDate" : ISODate("2016-05-15T09:31:48Z"),
            "lastHeartbeat" : ISODate("2016-05-15T09:32:05.125Z"),
            "lastHeartbeatRecv" : ISODate("2016-05-15T09:32:06.134Z"),
            "pingMs" : NumberLong(0),
            "electionTime" : Timestamp(0, 0),
            "electionDate" : ISODate("1970-01-01T00:00:00Z"),
            "configVersion" : 4
        },
        {
            "_id" : 1,
            "name" : "192.168.199.164:27018",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 11665,
            "optime" : {
                "ts" : Timestamp(1463304708, 2),
                "t" : NumberLong(3)
            },
            "optimeDate" : ISODate("2016-05-15T09:31:48Z"),
            "syncingTo" : "192.168.199.164:27019",
            "configVersion" : 4,
            "self" : true
        },
        {
            ......
        }
    ],
    "ok" : 1
}

可以看到27017成为了主节点Primary。

添加仲裁节点

添加仲裁节点与添加数据节点是一样的。只是在添加时需调用:

#创建文件夹
$ sudo mkdir /data/arb
#添加一个实例。为了最大限度地减少创建默认的数据,请设置--journal=false,在指定--dbpath有效
$ sudo mongod --port 27020 --dbpath --journal=false /data/arb --replSet rs &
#添加仲裁节点
$ rs.addArb("192.68.199.163:27020")

副本集要求参与选举投票(vote)的节点数为奇数。当我门数据集节点为偶数时,可以添加一个仲裁节点组成奇数。仲裁节点只参加投票不拥有数据,它对物理资源需要很少。
通过实际测试发现,当整个副本集集群中达到50%的节点(包括仲裁节点)不可用的时候,剩下的节点只能成为secondary节点,整个集群只能读不能 写。比如集群中有1个primary节点,2个secondary节点,加1个arbit节点时:当两个secondary节点挂掉了,那么剩下的原来的 primary节点也只能降级为secondary节点;当集群中有1个primary节点,1个secondary节点和1个arbit节点,这时即使 primary节点挂了,剩下的secondary节点也会自动成为primary节点。因为仲裁节点不复制数据,因此利用仲裁节点可以实现最少的机器开销达到两个节点热备的效果。

添加备份节点

hidden(成员用于支持专用功能):这样设置后此机器在读写中都不可见,并且不会被选举为Primary,但是可以投票,一般用于备份数据。
把27019节点删除,重启。再添加让其为hidden节点:

$ rs.add({"_id":2,"host":"192.168.199.164:27019","priority":0,"hidden":true})

添加延迟节点

Delayed(成员用于支持专用功能):可以指定一个时间延迟从primary节点同步数据。主要用于处理误删除数据马上同步到从节点导致的不一致问题。

$ rs.add({"_id":2,"host":"192.168.199.164:27019","priority":0,"hidden":true,"slaveDelay":60})    #单位 s

除了上面的一些节点,还有:

  • Secondary-Only:不能成为primary节点,只能作为secondary副本节点,防止一些性能不高的节点成为主节点。
  • Non-Voting:没有选举权的secondary节点,纯粹的备份数据节点。

具体如下:

角色 primary(能否) 客户端可见 参与投票 延迟同步 复制数据
Default X
Secondary-Only X X
Hidden X X X
Delayed X
Arbiters X X X X
Non-Voting X X

读写分离

MongoDB副本集对读写分离的支持是通过Read Preferences特性进行支持的,这个特性非常复杂和灵活。设置读写分离需要先在从节点SECONDARY 设置 setSlaveOk
应用程序驱动通过read reference来设定如何对副本集进行读取操作,默认的,客户端驱动所有的读操作都是直接访问primary节点的,从而保证了数据的严格一致性。
有如下几种模式:

模式 描述
primary 主节点,默认模式,读操作只在主节点,如果主节点不可用,报错或者抛出异常。
primaryPreferred 首选主节点,大多情况下读操作在主节点,如果主节点不可用,如故障转移,读操作在从节点。
secondary 从节点,读操作只在从节点, 如果从节点不可用,报错或者抛出异常。
secondaryPreferred 首选从节点,大多情况下读操作在从节点,特殊情况(如单主节点架构)读操作在主节点。
nearest 最邻近节点,读操作在最邻近的成员,可能是主节点或者从节点,关于最邻近的成员请参考官网nearest

附: 节点状态

名称 描述
STARTUP 没有任何活跃的节点,所有节点在这种状态下启动,解析副本集配置
PRIMARY 副本集的主节点
SECONDARY 副本集从节点,可以读数据
RECOVERING 可以投票,成员执行启动自检,或完成回滚或重新同步。
STARTUP2 节点加入,并运行初始同步
UNKNOWN 从其它节点看来,该节点未知
ARBITER 仲裁者,不复制数据,供投票
DOWN 在其它节点看来,该节点不可达
ROLLBACK 该节点正在执行回滚,不能读取数据
REMOVED 该节点被删除

mongo shell中复制相关方法

方法名 描述
rs.add() 添加节点到副本集
rs.addArb() 添加仲裁节点到副本集
rs.conf() 获取副本集的配置文档
rs.freeze() 指定一段时间内,当前节点不能竞选主节点Primary
rs.help() 获取副本集的基本方法
rs.initiate() 初始化一个新的副本集
rs.printReplicationInfo() 打印副本集的主节点Primary的状态报告
rs.printSlaveReplicationInfo() 打印副本集的从节点Secondary的状态报告
rs.reconfig() 重新配置副本集
rs.remove() 删除一个节点
rs.slaveOk() 设置当前连接可读,使用readPref() 和 MongosetReadPref()去设置读偏好
rs.status() 返回副本集状态信息的文档
rs.stepDown() 强制当前主节点Primary成为从节点Secondary,并触发投票选举
rs.syncFrom() 设置新的同步目标,覆盖默认的同步目标

复制 数据库的命令

名称 描述
replSetFreeze 阻止当前节点竞争当主节点Primary一段时间
replSetGetStatus 返回副本集状态信息的文档
replSetInitiate 初始化一个新的副本集
replSetMaintenance 启用或禁用维护模式,使从节点Secondary进入恢复状态
replSetReconfig 重新配置副本集
replSetStepDown 强制当前主节点Primary成为从节点Secondary,并触发投票选举
replSetSyncFrom 设置新的同步目标,覆盖默认的同步目标
resync 强制重新同步,仅在主从同步有效
applyOps 内部命令,应用oplog到当前的数据集
isMaster 显示该节点否是主节点和其它的相关信息
replSetGetConfig 返回副本集的配置对象

本文链接:参与评论 »

--EOF--

提醒:本文最后更新于 464 天前,文中所描述的信息可能已发生改变,请谨慎使用。

专题「Database 相关技术」的其它文章 »

Comments