目录

1·MongoDB 复制集概述
2·MongoDB 复制集部署
3·MongoDB 复制集管理(添加、移除等)
4·复制集的总结


MongoDB 复制集是什么?

之前的一片文章讲了 MongoDB 的安装和日常的操作,有兴趣的朋友可以看看 MongoDB 的安装和简单操作


1)什么是复制集?

复制集是额外的一种数据副本,是跨多个服务器同步数据的过程,通俗的说就是可以在不同的服务器上备份数据,专业点说就是冗灾处理。通过复制集可以对硬件和终端的服务进行恢复。


2)复制集的优势如下:

  1-让数据更加安全---(肯定啊,都是备份了,能不安全吗!)
  2-数据的高可用性---(可以全年无休,7*24小时×××)
  3-灾难恢复 ----(不用多说,既然是数据,能没有恢复吗)
  4-无停机维护---(比如说,备份、索引的重建等,MySQL 备份就需要锁定 表 、或者行,而它不会)
  5-读缩放----(额外的副本读取)
  6-它对应用程序是透明的

3)复制集工作原理是什么?

1·既然是一个功能性的东西,那么存在肯定有它的工作原理和过程。MongoDB 的复制集至少需要两个节点,是至少。其中一个是主节点 (Primary),这个主节点主要负责处理客户端的请求,其他的都是从节点 (Secondary),负责复制主节点上的数据。


2·那么我们常用的搭配方式有:一主一丛或一主多从,因为复制集的主从的选举原理是从节点不允许选举为当主节点,但是在实际的生产环境中,主服务器是不可能单点挂上去的,这样要是主服务器挂掉了,那就凉凉。


3·客户端主节点写入数据,在从节点读取数据,主节点与从节点进行数据交互保障数据的统一性。如果其中一个节点出现故障,其他节点马上会把业务接过来而无需停机操作。


4·下图是 MongoDB 复制集结构图


【亲测】教你如何搭建 MongoDB 复制集 + 选举原理_第1张图片


它的特点如下:

N个节点的群集
任何节点可作为主节点
所有写入都是在主节点,读取是在从节点,实现读写分离
自动故障转移
自动恢复


MongoDB 复制集部署

先介绍下环境:CenOS 7.4 上部署
部署前提:安装 MongoDB ,了解什么是实列、和创建多个实例
这里安装 MongoDB 就不再演示。我是直接 YUM 装的。
需要了解 MongoDB 安装 和 多实例的创建 请访问:MongoDB 的安装和命令
需要了解 MongoDB 与 MySQL 的区别 请访问:MySQL - mmm 高可用集群
需要了解 MySQL 的读写分离 请访问:MySQL 主从复制 + 读写分离


1)看多 MongoDB 的多实例创建的知道,创建实例前需要创建数据文件和日志文件存储路径。

[root@localhost ~]# mkdir -p /data/mongodb/mongodb{2,3,4} ----(创建3个数据文件,因为自己 yum 安装了一个,所以也算一个。)


[root@localhost ~]# mkdir -p /data/mongodb/logs ----(创建日志文件路径)
[root@localhost ~]# cd /data/mongodb/logs/
[root@localhost logs]# touch mongodb{2,3,4}.log -----(创建日志文件夹)
[root@localhost logs]# chmod 777 *.log -----(给最大的权限)


2)修改主配置文件 ---- (需要注意的地方用加粗方式显示)

[root@localhost ~]# vim /etc/mongod.conf ----(修改主配置文件)
修改内容如下:
#network interfaces
net:

port: 27017 ----(默认端口号)
bindIp: 0.0.0.0 # Listen to local interface only, comment to listen on all interfaces. ------------(修改监听地址)

#security:

#operationProfiling:

replication: -----------------(这里需要去掉注释)
replSetName: kgcrs ------------(在这里需要添加复制集的名字)


3)配置多实列 2 ,并且修改数据存储路径和日志文件

[root@localhost ~]# cp -p /etc/mongod.conf /etc/mongod2.conf ----(把主配置文件复制一份)


[root@localhost ~]# vim /etc/mongod2.conf -----(修改实列的配置文件)
修改内容如下: ---(修改内容以加粗方式显示)
systemLog:
destination: file
logAppend: true

path: /data/mongodb/logs/mongod2.log ----(日志文件的位子,这里需要注意路径,不能写错,之前创建的位置也不能有错)

#Where and how to store data.
storage:

dbPath: /data/mongodb/mongodb2 ----(数据存储路径,之前创建的路径必须一样)
journal:
enabled: true
#engine:
#mmapv1:
#wiredTiger:

#how the process runs
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile
timeZoneInfo: /usr/share/zoneinfo

#network interfaces
net:

port: 27018 -----(端口号需要修改为不一样的,自己能记住就行。)
bindIp: 0.0.0.0 # Listen to local interface only, comment to listen on all interfaces.

#security:

#operationProfiling:

replication:
replSetName: kgcrs ——(复制集的名称都需要一致,名字可以自己修改)


4)配置多实例 3、4 (和上一步都是一样,只是数据路径,和日志文件,还有端口号需要修改)

[root@localhost mongodb]# cp -p /etc/mongod2.conf /etc/mongod3.conf
[root@localhost mongodb]# cp -p /etc/mongod2.conf /etc/mongod4.conf
这里就不再分别演示修改方式,会以一张图来说明:修改数据路径、日志文件、端口号


下图是多实例修改样本:
【亲测】教你如何搭建 MongoDB 复制集 + 选举原理_第2张图片


5)当配置文件都 OK 后,就可以把4个实例全部启动:

[root@localhost ~]# mongod -f /etc/mongod.conf ---(第一台实例,是自己用YUM 装的,可以看作一台实例。)


[root@localhost ~]# mongod -f /etc/mongod2.conf ----(第二台实例,需要注意,如果有报错,很大一部分原因都是配置文件有问题,仔细检查配置文件的路径等问题)


[root@localhost ~]# mongod -f /etc/mongod3.conf ----(第三台实例)


[root@localhost ~]# mongod -f /etc/mongod4.conf ----(第4台实例)


6)进入第一台实例,初始化复制集配置 ---(以上步骤只是创建实例,并不是复制集哦。)

[root@localhost ~]# mongo ---(因为第一台的端口默认是 27017,所以这里不用跟端口号,如果是其他实例就需要 加上 --port 27018)


cfg={"_id":"kgcrs","members":[{"_id":0,"host":"192.168.198.128:27017"},{"_id":1,"host":"192.168.198.128:27018"},{"_id":2,"host":"192.168.198.128:27019"}]}

这里需要说明此代码:这里包含了3个节点的复制集,格式不需要变,因为这里是实验环境,所以 IP 是相同的,在生产环境中,IP 地址是不一样的。这里只需要修改端口号就行,还有就是我们一共是4个实例,但是这里只有3个,还剩下一个是为之后演示添加节点。


rs.initiate(cfg) ----(重新加载一下复制集,之后会有如下图的解释)


kgcrs:PRIMARY> rs.status() -----(查看复制集状态)

"set" : "kgcrs",  ----(复制集名称)
"date" : ISODate("2018-09-16T04:29:54.105Z"),
"myState" : 1,
"term" : NumberLong(1),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
    "lastCommittedOpTime" : {
        "ts" : Timestamp(1537072188, 1),
        "t" : NumberLong(1)
    },
    "readConcernMajorityOpTime" : {
        "ts" : Timestamp(1537072188, 1),
        "t" : NumberLong(1)
    },
    "appliedOpTime" : {
        "ts" : Timestamp(1537072188, 1),
        "t" : NumberLong(1)
    },
    "durableOpTime" : {
        "ts" : Timestamp(1537072188, 1),
        "t" : NumberLong(1)
    }
},
"members" : [
    {
        "_id" : 0, 
        "name" : "192.168.198.128:27017",    -----(节点详细信息)
        "health" : 1,  -----(健康值为 “1” 说明是在线状态。“0” 为宕机状态)
        "state" : 1,
        "stateStr" : "PRIMARY",  ------(主节点 PRIMARY)
        "uptime" : 848,
        "optime" : {
            "ts" : Timestamp(1537072188, 1),
            "t" : NumberLong(1)
        },
        "optimeDate" : ISODate("2018-09-16T04:29:48Z"),
        "syncingTo" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "infoMessage" : "could not find member to sync from",
        "electionTime" : Timestamp(1537072157, 1),
        "electionDate" : ISODate("2018-09-16T04:29:17Z"),
        "configVersion" : 1,
        "self" : true,
        "lastHeartbeatMessage" : ""
    },
    {
        "_id" : 1,
        "name" : "192.168.198.128:27018",------(第二台节点)
        "health" : 1,
        "state" : 2,
        "stateStr" : "SECONDARY",  -----(**从节点SECONDARY )**
        "uptime" : 47,
        "optime" : {
            "ts" : Timestamp(1537072188, 1),
            "t" : NumberLong(1)
        },
        "optimeDurable" : {
            "ts" : Timestamp(1537072188, 1),
            "t" : NumberLong(1)
        },
        "optimeDate" : ISODate("2018-09-16T04:29:48Z"),
        "optimeDurableDate" : ISODate("2018-09-16T04:29:48Z"),
        "lastHeartbeat" : ISODate("2018-09-16T04:29:53.346Z"),
        "lastHeartbeatRecv" : ISODate("2018-09-16T04:29:53.725Z"),
        "pingMs" : NumberLong(0),
        "lastHeartbeatMessage" : "",
        "syncingTo" : "192.168.198.128:27017",
        "syncSourceHost" : "192.168.198.128:27017",
        "syncSourceId" : 0,
        "infoMessage" : "",
        "configVersion" : 1
    },
    {
        "_id" : 2,
        "name" : "192.168.198.128:27019",------(第三台节点)
        "health" : 1,
        "state" : 2,
        "stateStr" : "SECONDARY", ------(从节点)
        "uptime" : 47,
        "optime" : {
            "ts" : Timestamp(1537072188, 1),
            "t" : NumberLong(1)
        },
        "optimeDurable" : {
            "ts" : Timestamp(1537072188, 1),
            "t" : NumberLong(1)
        },
        "optimeDate" : ISODate("2018-09-16T04:29:48Z"),
        "optimeDurableDate" : ISODate("2018-09-16T04:29:48Z"),
        "lastHeartbeat" : ISODate("2018-09-16T04:29:53.346Z"),
        "lastHeartbeatRecv" : ISODate("2018-09-16T04:29:53.725Z"),
        "pingMs" : NumberLong(0),
        "lastHeartbeatMessage" : "",
        "syncingTo" : "192.168.198.128:27017",
        "syncSourceHost" : "192.168.198.128:27017",
        "syncSourceId" : 0,
        "infoMessage" : "",
        "configVersion" : 1
    }
],
"ok" : 1,
"operationTime" : Timestamp(1537072188, 1),
"$clusterTime" : {
    "clusterTime" : Timestamp(1537072188, 1),
    "signature" : {
        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
        "keyId" : NumberLong(0)
    }
}

}


7)到此复制集创建完毕,但是我们还有一台实例没有添加到复制集中,所以现在演示复制集的添加:

kgcrs:PRIMARY> rs.add("192.168.198.128:27020") ----(添加一台实列到复制集中)


{
"ok" : 1, ----(添加成功)
"operationTime" : Timestamp(1537072738, 2),
"$clusterTime" : {
"clusterTime" : Timestamp(1537072738, 2),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}


8)有添加,那么就有移除,下面会演示移除的命令:

kgcrs:PRIMARY> rs.remove("192.168.198.128:27020")


{
"ok" : 1, -----(移除成功)
"operationTime" : Timestamp(1537072949, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1537072949, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}


9)到这里实验成功,那么需要验证它的功能性,下面会模拟故障转移,在这里是看不出实验效果,只给方法。

[root@localhost ~]# ps aux | grep mongod ---(查看mongod的进程)
root 40778 0.6 6.4 1581996 64908 ? Sl 12:15 0:12 mongod -f /etc/mongod.conf
root 40822 0.6 6.1 1469704 61216 ? Sl 12:17 0:12 mongod -f /etc/mongod2.conf
root 40884 0.7 5.9 1504232 59000 ? Sl 12:18 0:11 mongod -f /etc/mongod3.conf
root 40912 0.5 5.3 1440660 53752 ? Sl 12:19 0:09 mongod -f /etc/mongod4.conf


[root@localhost ~]# kill -9 40778 ----(把进程 kill 掉 ,因为细心的朋友可以看到,40778 现在是主节点,把它 kill 掉,就是为了看其他节点会不会自动切换为主节点)


10)进入第二台实列,查看复制集状态

[root@localhost ~]# mongo --port 27018 ----(进入第二台实例,这里需要跟上端口)
kgcrs:SECONDARY> rs.status() ------(再次查看复制集状态)


{
"set" : "kgcrs",
"date" : ISODate("2018-09-16T04:51:37.669Z"),
"myState" : 2,
"term" : NumberLong(2),
"syncingTo" : "192.168.198.128:27019",
"syncSourceHost" : "192.168.198.128:27019",
"syncSourceId" : 2,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1537073492, 1),
"t" : NumberLong(2)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1537073492, 1),
"t" : NumberLong(2)
},
"appliedOpTime" : {
"ts" : Timestamp(1537073492, 1),
"t" : NumberLong(2)
},
"durableOpTime" : {
"ts" : Timestamp(1537073492, 1),
"t" : NumberLong(2)
}
},
"members" : [
{
"_id" : 0,
"name" : "192.168.198.128:27017", ----(第一台节点)
"health" : 0, ----(原来的健康值变为 “0”)
"state" : 8,
"stateStr" : "(not reachable/healthy)",
"uptime" : 0,
"optime" : {
"ts" : Timestamp(0, 0),
"t" : NumberLong(-1)
},
"optimeDurable" : {
"ts" : Timestamp(0, 0),
"t" : NumberLong(-1)
},
"optimeDate" : ISODate("1970-01-01T00:00:00Z"),
"optimeDurableDate" : ISODate("1970-01-01T00:00:00Z"),
"lastHeartbeat" : ISODate("2018-09-16T04:51:36.332Z"),
"lastHeartbeatRecv" : ISODate("2018-09-16T04:47:29.932Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "Connection refused",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"configVersion" : -1
},
{
"_id" : 1, ----(第二台实例)
"name" : "192.168.198.128:27018",
"health" : 1, ----(健康值为 “1”
"state" : 2,
"stateStr" : "SECONDARY", ----(节点状态是:从几从节点)
"uptime" : 2060,
"optime" : {
"ts" : Timestamp(1537073492, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2018-09-16T04:51:32Z"),
"syncingTo" : "192.168.198.128:27019",
"syncSourceHost" : "192.168.198.128:27019",
"syncSourceId" : 2,
"infoMessage" : "",
"configVersion" : 3,
"self" : true,
"lastHeartbeatMessage" : ""
},
{
"_id" : 2, -----(第三台节点)
"name" : "192.168.198.128:27019",
"health" : 1, -----(健康值为 ‘1’)
"state" : 1,
"stateStr" : "PRIMARY", -----(现在是第三台节点为主节点)
"uptime" : 1349,
"optime" : {
"ts" : Timestamp(1537073492, 1),
"t" : NumberLong(2)
},
"optimeDurable" : {
"ts" : Timestamp(1537073492, 1),
"t" : NumberLong(2)
},
"optimeDate" : ISODate("2018-09-16T04:51:32Z"),
"optimeDurableDate" : ISODate("2018-09-16T04:51:32Z"),
"lastHeartbeat" : ISODate("2018-09-16T04:51:36.289Z"),
"lastHeartbeatRecv" : ISODate("2018-09-16T04:51:37.431Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1537073261, 1),
"electionDate" : ISODate("2018-09-16T04:47:41Z"),
"configVersion" : 3
}
],
"ok" : 1,
"operationTime" : Timestamp(1537073492, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1537073492, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}


11)从以上数据可以看出,当主节点挂掉后,从节点会自动切换为主节点,但是又不是按照顺序来推荐谁当主节点,这里的推荐的原理,后面会讲到。当然,这里是自动切换主从,我们也可以手动进行切换

[root@localhost ~]# mongod -f /etc/mongod.conf ----(把上个步骤停止的实例启动起来,方便我们实验)
[root@localhost ~]# mongo --port 27019 ----(进入主节点服务器,因为我们需要来进行手动切换主从。)


kgcrs:PRIMARY> rs.freeze(30) ----(暂停30秒不参加选举)
kgcrs:PRIMARY> rs.stepDown(60,30) ----(让主节点交出位子,维持从节点状态不少于60秒,同时等待30秒使主节点和从节点日志同步)
kgcrs:SECONDARY> rs.status() ----(再次查看复制集状态)


12)在从节点默认的情况下,我们是无法在从节点上读取数据的,只有执行以下命令:

kgcrs:SECONDARY> rs.slaveOk() ----(执行此命令才可以在从节点上读取数据,否则是不允许读取)


总结:

1·复制集信息要点说明:health 为 “1” 代表健康,“0” 代表宕机。state 为 “1” 代表主节点,为 “2” 代表从节点


2·在配置实例时需要特别注意修改的数据存储路径,日志存储位子,端口号的修改。


3·复制集最少需要两个节点,这里主节点复制处理客户端请求,从节点复制主节点上的数据


4·它可以实现群集的高可用,当故障时会自动切换,管理员也可以手动切换。


5·在复制集初始化配置时要保证从节点上没有数据。


6·在初始化完复制集参数后,需要通过命令 :rs.initate(cfg) 命令启动复制集。