一,概述
MongoDB复制是将数据同步在多个服务器的过程。
复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。
复制还允许您从硬件故障和服务中断中恢复数据。
二,什么是复制
- 保障数据的安全性
- 数据高可用性 (24*7)
- 灾难恢复
- 无需停机维护(如备份,重建索引,压缩)
- 分布式读取数据
三,MongoDB复制原理
mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。
mongodb各个节点常见的搭配方式为:一主一从、一主多从。
MongoDB复制结构图如下所示:
以上结构图总,客户端总主节点读取数据,在客户端写入数据到主节点是, 主节点与从节点进行数据交互保障数据的一致性。
复制集特征
- N 个节点的集群
- 任何节点可作为主节点
- 所有写入操作都在主节点上
- 自动故障转移
- 自动恢复
四,部署复制集
这里使用Windows平台部署,基于MongoDB 3.4版本。
创建复制集中每个节点存放数据的目录。
D:\MongoDB\Server\3.4\data\db_rs0\data\rs0_0
D:\MongoDB\Server\3.4\data\db_rs0\data\rs0_1
D:\MongoDB\Server\3.4\data\db_rs0\data\rs0_2
创建复制集中每个节点的日志文件。
D:\MongoDB\Server\3.4\data\db_rs0\data\configs_rs0\rs0_0.conf
D:\MongoDB\Server\3.4\data\db_rs0\data\configs_rs0\rs0_1.conf
D:\MongoDB\Server\3.4\data\db_rs0\data\configs_rs0\rs0_2.conf
创建复制集中的每个节点启动时所需的配置文件。
dbpath = D:\MongoDB\Server\3.4\data\db_rs0\data\rs0_0
logpath = D:\MongoDB\Server\3.4\data\db_rs0\data\logs\rs0_0.log
journal = true
port = 40000
replSet = rs0
文件中dbpath指向数据库数据文件存放的路径(第1步中已经创建好),logpath指向数据库的日志文件路径(第2步中已创建好),journal表示对于此mongod实例是否启动日志功能,port为实例监听的端口号,rs0为实例所在的复制集名称,更多参数意思可以参考MongoDB手册。
第二个节点配置文件为D:\MongoDB\Server\3.4\data\db_rs0\data\configs_rs0\rs0_1.conf,内容如下所示。
dbpath = D:\MongoDB\Server\3.4\data\db_rs0\data\rs0_1
logpath = D:\MongoDB\Server\3.4\data\db_rs0\data\logs\rs0_1.log
journal = true
port = 40001
replSet = rs0
第三个节点配置文件为D:\MongoDB\Server\3.4\data\db_rs0\data\configs_rs0\rs0_2.conf,内容如下所示。
dbpath = D:\MongoDB\Server\3.4\data\db_rs0\data\rs0_2
logpath = D:\MongoDB\Server\3.4\data\db_rs0\data\logs\rs0_2.log
journal = true
port = 40002
replSet = rs0
启动上三个节点对应的MongoDB实例。
mongod -config D:\MongoDB\Server\3.4\data\db_rs0\data\configs_rs0\rs0_0.conf
mongod -config D:\MongoDB\Server\3.4\data\db_rs0\data\configs_rs0\rs0_1.conf
mongod -config D:\MongoDB\Server\3.4\data\db_rs0\data\configs_rs0\rs0_2.conf
现在已经启动了3个实例,但是复制集还没配置好,如上图所描述的那样,我们应该通过配置确定哪个节点为primary,哪个节点为secondary。
启动一个mongo客户端,连接到上面的一个mongod实例。
mongo -port 40000
结果:
这个时候的复制集只有刚才这个初始化的成员,通过如下命令查看。
rs.conf()
结果:
按照MongoDB的默认设置,刚才执行初始化命令的这个mongod实例将成为复制集中的primary节点。
添加复制集中的secondary节点,继续在上面的mongod实例上执行如下命令。
观察整个复制集的状态信息。
rs.status()
结果为:
/* 1 */
{
"set" : "rs0",//复制集的名称
"date" : ISODate("2017-06-11T09:48:41.400Z"),
"myState" : 1,//当前节点成员所在复制集中的位置,如1表示primary,2表示secondary
"term" : NumberLong(1),
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1497174514, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1497174514, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1497174514, 1),
"t" : NumberLong(1)
}
},
"members" : [ //复制集的所有成员信息
{
"_id" : 0,//成员编号
"name" : "VICTOR-PC:40000",//成员所在的服务器名称
"health" : 1.0,//成员在复制集中是否运行,1表示运行,0表示失败
"state" : 1,//成员在复制集中的状态,1是primary
"stateStr" : "PRIMARY",//成员在复制集中的状态名称
"uptime" : 1259,//成员的在线时间,单位是秒
"optime" : {
"ts" : Timestamp(1497174514, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2017-06-11T09:48:34.000Z"),
"electionTime" : Timestamp(1497173622, 1),
"electionDate" : ISODate("2017-06-11T09:33:42.000Z"),
"configVersion" : 3,
"self" : true//成员为当前命令所在的服务器
},
{
"_id" : 1,
"name" : "VICTOR-PC:40001",
"health" : 1.0,
"state" : 2,//成员所在复制集中的状态,2是secondary
"stateStr" : "SECONDARY",
"uptime" : 119,
"optime" : {
"ts" : Timestamp(1497174514, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1497174514, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2017-06-11T09:48:34.000Z"),
"optimeDurableDate" : ISODate("2017-06-11T09:48:34.000Z"),
"lastHeartbeat" : ISODate("2017-06-11T09:48:41.231Z"),
"lastHeartbeatRecv" : ISODate("2017-06-11T09:48:40.686Z"),
"pingMs" : NumberLong(0),//此远端成员到本实例间一个路由包的来回时间
"syncingTo" : "VICTOR-PC:40000",//此成员需要从哪个实例同步数据
"configVersion" : 3
},
{
"_id" : 2,
"name" : "VICTOR-PC:40002",
"health" : 1.0,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 106,
"optime" : {
"ts" : Timestamp(1497174514, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1497174514, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2017-06-11T09:48:34.000Z"),
"optimeDurableDate" : ISODate("2017-06-11T09:48:34.000Z"),
"lastHeartbeat" : ISODate("2017-06-11T09:48:41.231Z"),
"lastHeartbeatRecv" : ISODate("2017-06-11T09:48:39.968Z"),
"pingMs" : NumberLong(0),
"syncingTo" : "VICTOR-PC:40001",
"configVersion" : 3
}
],
"ok" : 1.0
}
上面复制集状态信息的输出是基于primary实例的,也可以在secondary实例上输出复制集的状态信息,包含的字段与上面大致相同。
五,移除复制集节点
这里提供两个移除复制集节点的方法。
使用 rs.remove() 来移除节点
- 关闭我们想要移除的 mongod 实例,可以通过在
mongo -port 端口号
连接要移除的mongod实例,执行 db.shutdownServer() 来关闭。 - 连接到复制集现在的 primary的mongod实例。我们可以连接到任意一个复制集节点并执行 db.isMaster() 来确认是否为主节点。
- 通过 rs.remove() 来移除节点。
我们以上面创建的复制集为例,删除40002端口的节点。
连接要移除的节点
连接primary节点
通过 rs.reconfig() 来移除节点
- 关闭我们想要移除的 mongod 实例,可以通过在
mongo -port 端口号
连接要移除的mongod实例,执行 db.shutdownServer() 来关闭。 - 连接到复制集现在的 primary的mongod实例。我们可以连接到任意一个复制集节点并执行 db.isMaster() 来确认是否为主节点。
- 使用 rs.conf() 来查看现在的复制集配置,并记住我们需要移除的节点的 members 数组的位置。
>rs.conf()
/* 1 */
{
"_id" : "rs0",
"version" : 5,
"protocolVersion" : NumberLong(1),
"members" : [
{
"_id" : 0,
"host" : "VICTOR-PC:40000",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1.0,
"tags" : {},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 1,
"host" : "VICTOR-PC:40001",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1.0,
"tags" : {},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "VICTOR-PC:40002",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1.0,
"tags" : {},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
"settings" : {
"chainingAllowed" : true,
"heartbeatIntervalMillis" : 2000,
"heartbeatTimeoutSecs" : 10,
"electionTimeoutMillis" : 10000,
"catchUpTimeoutMillis" : 2000,
"getLastErrorModes" : {},
"getLastErrorDefaults" : {
"w" : 1,
"wtimeout" : 0
},
"replicaSetId" : ObjectId("593d1c42196120213b965f1f")
}
}
- 将现在的的配置赋值给 cfg。
cfg = rs.conf()
- 修改 cfg 来移除节点。
//移除VICTOR-PC:40002节点
cfg.members.splice(2,1)
- 通过如下命令将新的配置覆盖应用在复制集用。
rs.reconfig(cfg)
- 通过 rs.conf() 命令来再确认新的配置。
/* 1 */
{
"_id" : "rs0",
"version" : 6,
"protocolVersion" : NumberLong(1),
"members" : [
{
"_id" : 0,
"host" : "VICTOR-PC:40000",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1.0,
"tags" : {},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 1,
"host" : "VICTOR-PC:40001",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1.0,
"tags" : {},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
"settings" : {
"chainingAllowed" : true,
"heartbeatIntervalMillis" : 2000,
"heartbeatTimeoutSecs" : 10,
"electionTimeoutMillis" : 10000,
"catchUpTimeoutMillis" : 2000,
"getLastErrorModes" : {},
"getLastErrorDefaults" : {
"w" : 1,
"wtimeout" : 0
},
"replicaSetId" : ObjectId("593d1c42196120213b965f1f")
}
}
六,添加仲裁者节点
添加仲裁者节点跟添加普通节点思路大致相同。
创建节点存储数据的目录。
D:\MongoDB\Server\3.4\data\db_rs0\data\rs0_3
创建节点的日志文件。
D:\MongoDB\Server\3.4\data\db_rs0\data\logs\rs0_3.log
创建节点启动时所需的配置文件。
D:\MongoDB\Server\3.4\data\db_rs0\data\configs_rs0\rs0_3.conf
配置文件内容
dbpath = D:\MongoDB\Server\3.4\data\db_rs0\data\rs0_3
logpath = D:\MongoDB\Server\3.4\data\db_rs0\data\logs\rs0_3.log
journal = true
port = 40003
replSet = rs0
启动节点的MongoDB实例
mongod -config D:\MongoDB\Server\3.4\data\db_rs0\data\configs_rs0\rs0_3.conf
primary节点加入仲裁者节点
rs.addArb("VICTOR-PC:40003")
查看效果。
>rs.conf()
/* 1 */
{
"_id" : "rs0",
"version" : 7,
"protocolVersion" : NumberLong(1),
"members" : [
{
"_id" : 0,
"host" : "VICTOR-PC:40000",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1.0,
"tags" : {},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 1,
"host" : "VICTOR-PC:40001",
"arbiterOnly" : false,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1.0,
"tags" : {},
"slaveDelay" : NumberLong(0),
"votes" : 1
},
{
"_id" : 2,
"host" : "VICTOR-PC:40003",
"arbiterOnly" : true,
"buildIndexes" : true,
"hidden" : false,
"priority" : 1.0,
"tags" : {},
"slaveDelay" : NumberLong(0),
"votes" : 1
}
],
"settings" : {
"chainingAllowed" : true,
"heartbeatIntervalMillis" : 2000,
"heartbeatTimeoutSecs" : 10,
"electionTimeoutMillis" : 10000,
"catchUpTimeoutMillis" : 2000,
"getLastErrorModes" : {},
"getLastErrorDefaults" : {
"w" : 1,
"wtimeout" : 0
},
"replicaSetId" : ObjectId("593d1c42196120213b965f1f")
}
}
七,将单节点转为复制集
关闭 standalone mongod 实例。
重启实例,并使用 –replSet 参数来指定复制集的名字。
mongod -config 配置文件路径 --replSet rs0
剩下的步骤跟上面添加节点一致。
八,更换复制集节点
我们可以通过下列命令来将复制集的members[0]节点的主机名修改为 “VICTOR-PC:40004”
cfg = rs.conf()
cfg.members[0].host = "VICTOR-PC:40004"
rs.reconfig(cfg)
注意:
复制集的任何配置变动都会使 primary 降职,并进行 election 。在选举过程中,所有已经建立的链接将会释放,这将返回一个错误,及时操作执行成功了。
九,参考
更多详细内容可以参考MongoDB官文文档https://docs.mongodb.com/manual/replication/