MongoDB主从复制

本文将介绍 MongoDB 怎样配置主从复制。

MongoDB 主从复制是将数据同步到多个 MongoDB 服务器的过程。复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性, 并可以保证数据的安全性。复制还允许您从硬件故障和服务中断中恢复数据。

主从复制是 MongoDB 最常用的复制方式,也是一个简单的数据库同步备份的集群技术。这种方式很灵活,可用于备份、故障恢复、读扩展等。

MongoDB主从复制原理

MongoDB 的主从复制至少需要两个节点。其中一个是主节点(Primary),负责处理客户端请求,其余的都是从节点(Secondary),负责复制主节点上的数据。

MongoDB 节点常见的搭配方式为:

  • 一主一从

  • 一主多从

主节点记录在其上的所有操作日志,从节点定期轮询主节点获取这些操作。然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。MongoDB 主从复制结构图如下所示:

MongoDB主从复制

以上结构图中,客户端从主节点(Primary)读取数据,在客户端写入数据到主节点(Primary)时, 主节点与从节点进行数据交互保障数据的一致性。

实例

下面将介绍一个实例来了解 MongoDB 的主从复制。步骤如下:

(1)服务器准备

在开始之前,我们准备了 3 个 CentOS6.4 系统(均为VMWare虚拟机)。服务器IP地址和主机名分别如下:

  • 主节点(S1):192.168.238.201

  • 从节点(S2):192.168.238.202

  • 从节点(S3):192.168.238.203

(2)使用 service 启动 MongoDB 服务

在开始启动 MongoDB 时,需要先安装 MongoDB,采用 mongodb-org-unstable-server-4.3.3-1.el6.x86_64.rpm 包进行安装。然后分别去配置每个 MongoDB 服务。 

a、配置和启动主节点S1

主节点的配置信息如下:

systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
storage:
  dbPath: /var/lib/mongo
  journal:
    enabled: true
processManagement:
  fork: true
  pidFilePath: /var/run/mongodb/mongod.pid
  timeZoneInfo: /usr/share/zoneinfo
net:
  port: 27017
  bindIp: localhost,192.168.238.201
# 配置复制集
replication:
  replSetName: rs0

启动主节点,如下:

[root@S1 lib]# service mongod start
Starting mongod:                                           [  OK  ]

b、配置和启动从节点S2

主节点的配置信息如下:

systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
storage:
  dbPath: /var/lib/mongo
  journal:
    enabled: true
processManagement:
  fork: true
  pidFilePath: /var/run/mongodb/mongod.pid
  timeZoneInfo: /usr/share/zoneinfo
net:
  port: 27017
  bindIp: localhost,192.168.238.202
# 配置复制集
replication:
  replSetName: rs0

启动主节点,如下:

[root@S2 lib]# service mongod start
Starting mongod:                                           [  OK  ]


c、配置和启动从节点S3

主节点的配置信息如下:

systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log
storage:
  dbPath: /var/lib/mongo
  journal:
    enabled: true
processManagement:
  fork: true
  pidFilePath: /var/run/mongodb/mongod.pid
  timeZoneInfo: /usr/share/zoneinfo
net:
  port: 27017
  bindIp: localhost,192.168.238.203
# 配置复制集
replication:
  replSetName: rs0

启动主节点,如下:

[root@S3 lib]# service mongod start
Starting mongod:                                           [  OK  ]


(3)初始化MongoDB复制集合

使用 mongo 工具连接到主节点,如下:

mongo --host 192.168.238.201 --port 27017

在 Mongo 客户端使用命令 rs.initiate() 来启动一个新的副本集。如下:

> rs.initiate()
{
        "info2" : "no configuration specified. Using a default configuration for the set",
        "me" : "192.168.238.201:27017",
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1530620665, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1530620665, 1)
}
rs0:SECONDARY>
rs0:PRIMARY>

上面,ok 为 1,表示启动成功。启动成功后命令提示符为“rs0:SECONDARY>”按一下回车键将看见“rs0:PRIMARY>”。

此时,我们就可以使用 rs.add() 指令为 rs0 复制集添加成员。如下:

rs0:PRIMARY> rs.add("192.168.238.202:27017")
{
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1530620749, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1530620749, 1)
}
rs0:PRIMARY> rs.add("192.168.238.203:27017")
{
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1530620753, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1530620753, 1)
}

上面向 rs0 复制集中添加了两个成员,分别为 192.168.238.202 和 192.168.238.203。

你可以使用 rs.status() 指令查看 mongodb 状态。如下:

rs0:PRIMARY> rs.status()
{
        "set" : "rs0",
        "date" : ISODate("2018-07-03T12:26:04.252Z"),
        "myState" : 1,
        "term" : NumberLong(1),
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "heartbeatIntervalMillis" : NumberLong(2000),
        "majorityVoteCount" : 2,
        "writeMajorityCount" : 2,
        "optimes" : {
                "lastCommittedOpTime" : {
                        "ts" : Timestamp(1530620753, 1),
                        "t" : NumberLong(1)
                },
                "lastCommittedWallTime" : ISODate("2018-07-03T12:25:53.165Z"),
                "readConcernMajorityOpTime" : {
                        "ts" : Timestamp(1530620753, 1),
                        "t" : NumberLong(1)
                },
                "readConcernMajorityWallTime" : ISODate("2018-07-03T12:25:53.165Z"),
                "appliedOpTime" : {
                        "ts" : Timestamp(1530620753, 1),
                        "t" : NumberLong(1)
                },
                "durableOpTime" : {
                        "ts" : Timestamp(1530620753, 1),
                        "t" : NumberLong(1)
                },
                "lastAppliedWallTime" : ISODate("2018-07-03T12:25:53.165Z"),
                "lastDurableWallTime" : ISODate("2018-07-03T12:25:53.165Z")
        },
        "lastStableRecoveryTimestamp" : Timestamp(1530620716, 1),
        "electionCandidateMetrics" : {
                "lastElectionReason" : "electionTimeout",
                "lastElectionDate" : ISODate("2018-07-03T12:24:25.867Z"),
                "electionTerm" : NumberLong(1),
                "lastCommittedOpTimeAtElection" : {
                        "ts" : Timestamp(0, 0),
                        "t" : NumberLong(-1)
                },
                "lastSeenOpTimeAtElection" : {
                        "ts" : Timestamp(1530620665, 1),
                        "t" : NumberLong(-1)
                },
                "numVotesNeeded" : 1,
                "priorityAtElection" : 1,
                "electionTimeoutMillis" : NumberLong(10000),
                "newTermStartDate" : ISODate("2018-07-03T12:24:26.881Z"),
                "wMajorityWriteAvailabilityDate" : ISODate("2018-07-03T12:24:26.904Z")
        },
        "members" : [
                {
                        "_id" : 0,
                        "name" : "192.168.238.201:27017",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 159,
                        "optime" : {
                                "ts" : Timestamp(1530620753, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2018-07-03T12:25:53Z"),
                        "syncSourceHost" : "",
                        "syncSourceId" : -1,
                        "infoMessage" : "could not find member to sync from",
                        "electionTime" : Timestamp(1530620665, 2),
                        "electionDate" : ISODate("2018-07-03T12:24:25Z"),
                        "configVersion" : 3,
                        "self" : true,
                        "lastHeartbeatMessage" : ""
                },
                {
                        "_id" : 1,
                        "name" : "192.168.238.202:27017",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 14,
                        "optime" : {
                                "ts" : Timestamp(1530620753, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1530620753, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2018-07-03T12:25:53Z"),
                        "optimeDurableDate" : ISODate("2018-07-03T12:25:53Z"),
                        "lastHeartbeat" : ISODate("2018-07-03T12:26:03.170Z"),
                        "lastHeartbeatRecv" : ISODate("2018-07-03T12:26:03.693Z"),
                        "pingMs" : NumberLong(0),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "192.168.238.201:27017",
                        "syncSourceId" : 0,
                        "infoMessage" : "",
                        "configVersion" : 3
                },
                {
                        "_id" : 2,
                        "name" : "192.168.238.203:27017",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 11,
                        "optime" : {
                                "ts" : Timestamp(1530620753, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDurable" : {
                                "ts" : Timestamp(1530620753, 1),
                                "t" : NumberLong(1)
                        },
                        "optimeDate" : ISODate("2018-07-03T12:25:53Z"),
                        "optimeDurableDate" : ISODate("2018-07-03T12:25:53Z"),
                        "lastHeartbeat" : ISODate("2018-07-03T12:26:03.215Z"),
                        "lastHeartbeatRecv" : ISODate("2018-07-03T12:26:03.760Z"),
                        "pingMs" : NumberLong(6),
                        "lastHeartbeatMessage" : "",
                        "syncSourceHost" : "192.168.238.201:27017",
                        "syncSourceId" : 0,
                        "infoMessage" : "",
                        "configVersion" : 3
                }
        ],
        "ok" : 1,
        "$clusterTime" : {
                "clusterTime" : Timestamp(1530620753, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        },
        "operationTime" : Timestamp(1530620753, 1)
}

上面文档中的 members 字段是一个数组,保存了所有复制集中的节点信息。

(4)插入和查询数据

使用 mongo 客户端连接到主节点,插入数据。如下:

# 连接到主节点
mongo --host 192.168.238.201 --port 27017

# 向 test 数据库中的 test 集合插入一个文档
rs0:PRIMARY> db.test.insert({name:"test", version:"1.1.1"})
WriteResult({ "nInserted" : 1 })

再次,使用 mongo 客户端连接到从节点 192.168.238.202。如下:

# 连接到从节点
mongo --host 192.168.238.202 --port 27017

# 执行后允许进行读取操作
rs0:SECONDARY> rs.slaveOk()

# 显示所有的数据库
rs0:SECONDARY> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB

# 切换到 test 数据库
rs0:SECONDARY> use test
switched to db test

# 查询 test 集合中的文档
rs0:SECONDARY> db.test.find()
{ "_id" : ObjectId("5e5b884159de83945fb5d3c6"), "name" : "test", "version" : "1.1.1" }
rs0:SECONDARY>

连接到从节点 192.168.238.203,如下:

# 连接到从节点
mongo --host 192.168.238.203 --port 27017

# 执行后允许进行读取操作
rs0:SECONDARY> rs.slaveOk()

# 显示所有的数据库
rs0:SECONDARY> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB

# 切换到 test 数据库
rs0:SECONDARY> use test
switched to db test

# 显示 test 数据库中的所有集合
rs0:SECONDARY> show collections
test

# 查询 test 集合中的文档
rs0:SECONDARY> db.test.find()
{ "_id" : ObjectId("5e5b884159de83945fb5d3c6"), "name" : "test", "version" : "1.1.1" }

如果你在执行其他操作之前没有执行 rs.slaveOk() 命令,则会抛出如下错误:

rs0:SECONDARY> show dbs
2020-03-01T18:27:49.612+0800 E  QUERY    [js] uncaught exception: Error: listDatabases failed:{
        "topologyVersion" : {
                "processId" : ObjectId("5b381d08b1311857257fef9f"),
                "counter" : NumberLong(3)
        },
        "operationTime" : Timestamp(1530647569, 1),
        "ok" : 0,
        "errmsg" : "not master and slaveOk=false",
        "code" : 13435,
        "codeName" : "NotMasterNoSlaveOk",
        "$clusterTime" : {
                "clusterTime" : Timestamp(1530647569, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
} :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
Mongo.prototype.getDBs/<@src/mongo/shell/mongo.js:135:19
Mongo.prototype.getDBs@src/mongo/shell/mongo.js:87:12
shellHelper.show@src/mongo/shell/utils.js:906:13
shellHelper@src/mongo/shell/utils.js:790:15
@(shellhelp2):1:1

如果你在从节点中执行insert命令,则抛出如下错误:

rs0:SECONDARY> db.test.insert({name:"123"})
WriteCommandError({
        "topologyVersion" : {
                "processId" : ObjectId("5b381d08b1311857257fef9f"),
                "counter" : NumberLong(3)
        },
        "operationTime" : Timestamp(1530647929, 1),
        "ok" : 0,
        "errmsg" : "not master",
        "code" : 10107,
        "codeName" : "NotMaster",
        "$clusterTime" : {
                "clusterTime" : Timestamp(1530647929, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
})

注意:主节点才能插入和读取数据,从节点只能读取数据。

不是每一次努力都有收获,但是,每一次收获都必须努力。
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号