MongoDB 复制集(Replica Set)

本文涉及的产品
云数据库 MongoDB,通用型 2核4GB
简介: 复制集(replica Set)或者副本集是MongoDB的核心高可用特性之一,它基于主节点的oplog日志持续传送到辅助节点,并重放得以实现主从节点一致。

复制集(replica Set)或者副本集是MongoDB的核心高可用特性之一,它基于主节点的oplog日志持续传送到辅助节点,并重放得以实现主从节点一致。再结合心跳机制,当感知到主节点不可访问或宕机的情形下,辅助节点通过选举机制来从剩余的辅助节点中推选一个新的主节点从而实现自动切换。这个特性与MySQL MHA实现原理一样。本文主要描述MongoDB复制集并给出创建复制集示例以及完成自动切换。

一、复制集相关概念

复制集
    复制是在多台服务器之间同步数据的过程,由一组Mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点
    Mongodb Driver(客户端)的所有数据都写入Primary,Secondary从Primary同步写入的数据
    通过上述方式来保持复制集内所有成员存储相同的数据集,提供数据的高可用

复制的目的
        Failover (故障转移,故障切换,故障恢复)
        Redundancy(数据冗余)
        避免单点,用于灾难时恢复,报表处理,提升数据可用性
        读写分离,分担读压力
        对用户透明的系统维护升级

复制集的原理
        主节点记录所有的变更到oplog日志
        辅助节点(Secondary)复制主节点的oplog日志并且将这些日志在辅助节点进行重放(做)
        各个节点之间会定期发送心跳信息,一旦主节点宕机,则触发选举一个新的主节点,剩余的辅助节点指向新的主
        10s内各辅助节点无法感知主节点的存在,则开始触发选举
        通常1分钟内完成主辅助节点切换,10-30s内感知主节点故障,10-30s内完成选举及切换       

复制≠备份
        用户恢复数据,防止数据丢失,实现灾难恢复
        人为误操作导致数据删除,程序Bug导致数据损坏等

Primary
        首要复制节点,由选举产生,提供读写服务的节点,产生oplog日志

Secondary       
        备用(辅助)复制节点,Secondary可以提供读服务,增加Secondary节点可以提供复制集的读服务能力
        在故障时,备用节点可以根据设定的优先级别提升为首要节点。提升了复制集的可用性

Arbiter
        Arbiter节点只参与投票,不能被选为Primary,并且不从Primary同步数据     
        Arbiter本身不存储数据,是非常轻量级的服务。
        当复制集成员为偶数时,最好加入一个Arbiter节点,以提升复制集可用性

复制集示意图
这里写图片描述

二、创建复制集

# cat /etc/redhat-release 
CentOS release 6.7 (Final)
# mongod --version
db version v3.0.12
git version: 33934938e0e95d534cebbaff656cde916b9c3573

创建实例对应的数据目录
# mkdir -pv /data/{n1,n2,n3}

# mongod --replSet repSetTest --dbpath /data/n1 --logpath /data/n1/n1.log \
> --port 27000 --smallfiles --oplogSize 128 --fork
# mongod --replSet repSetTest --dbpath /data/n2 --logpath /data/n2/n2.log  \
> --port 27001 --smallfiles --oplogSize 128 --fork
# mongod --replSet repSetTest --dbpath /data/n3 --logpath /data/n3/n3.log  \
> --port 27002 --smallfiles --oplogSize 128 --fork

查看相应的端口
# netstat -nltp|grep mongod
tcp        0      0 0.0.0.0:27000               0.0.0.0:*                   LISTEN      5765/mongod         
tcp        0      0 0.0.0.0:27001               0.0.0.0:*                   LISTEN      5781/mongod         
tcp        0      0 0.0.0.0:27002               0.0.0.0:*                   LISTEN      5810/mongod  

连接到第一个实例
# mongo localhost:27000
MongoDB shell version: 3.0.12
connecting to: localhost:27000/test
> db.person.insert({name:'Fred', age:35})    //提示当前节点非master节点
WriteResult({ "writeError" : { "code" : undefined, "errmsg" : "not master" } })
> 
//下面我们添加复制集的配置文件
> cfg = {
...  '_id':'repSetTest',
...  'members':[
...  {'_id':0, 'host': 'localhost:27000'},
...  {'_id':1, 'host': 'localhost:27001'},
...  {'_id':2, 'host': 'localhost:27002'}
...  ]
... }
{
        "_id" : "repSetTest",
        "members" : [
                {
                        "_id" : 0,
                        "host" : "localhost:27000"
                },
                {
                        "_id" : 1,
                        "host" : "localhost:27001"
                },
                {
                        "_id" : 2,
                        "host" : "localhost:27002"
                }
        ]
}

//复制集通过replSetInitiate命令(或mongo shell的rs.initiate())进行初始化
//初始化后各个成员间开始发送心跳消息,并发起Priamry选举操作
//获得『大多数』成员投票支持的节点,会成为Primary,其余节点成为Secondary。
//通常建议将复制集成员数量设置为奇数,以确保在复制集故障的时候能够正确选举出Primary。
//对于复制集故障导致无法正确选举得到Primary的情形下,复制集将无法提供写服务,处于只读状态

> rs.initiate(cfg)  //初始化配置文件
{ "ok" : 1 }

//查看状态,以下提示27000为主节点,其余2个端口为辅助节点
repSetTest:OTHER> rs.status() 
{
        "set" : "repSetTest",
        "date" : ISODate("2016-08-30T05:41:15.302Z"),
        "myState" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:27000",
                        "health" : 1,             //健康状态:OK
                        "state" : 1,
                        "stateStr" : "PRIMARY",  //当前为主节点
                        "uptime" : 118,
                        "optime" : Timestamp(1472535666, 1),
                        "optimeDate" : ISODate("2016-08-30T05:41:06Z"),
                        "electionTime" : Timestamp(1472535670, 1),
                        "electionDate" : ISODate("2016-08-30T05:41:10Z"),
                        "configVersion" : 1,
                        "self" : true
                },
                {
                        "_id" : 1,
                        "name" : "localhost:27001",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 9,
                        "optime" : Timestamp(1472535666, 1),
                        "optimeDate" : ISODate("2016-08-30T05:41:06Z"),
                        "lastHeartbeat" : ISODate("2016-08-30T05:41:14.030Z"),
                        "lastHeartbeatRecv" : ISODate("2016-08-30T05:41:14.048Z"),
                        "pingMs" : 0,
                        "configVersion" : 1
                },
                {
                        "_id" : 2,
                        "name" : "localhost:27002",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 9,
                        "optime" : Timestamp(1472535666, 1),
                        "optimeDate" : ISODate("2016-08-30T05:41:06Z"),
                        "lastHeartbeat" : ISODate("2016-08-30T05:41:14.030Z"),
                        "lastHeartbeatRecv" : ISODate("2016-08-30T05:41:14.057Z"),
                        "pingMs" : 0,
                        "configVersion" : 1
                }
        ],
        "ok" : 1
}


//使用isMaster()函数寻找谁是Master
repSetTest:PRIMARY> db.isMaster()  
{
        "setName" : "repSetTest",
        "setVersion" : 1,
        "ismaster" : true,
        "secondary" : false,
        "hosts" : [
                "localhost:27000",
                "localhost:27001",
                "localhost:27002"
        ],
        "primary" : "localhost:27000",
        "me" : "localhost:27000",
        "electionId" : ObjectId("57c51c76d5963b4abbd1d72f"),
        "maxBsonObjectSize" : 16777216,
        "maxMessageSizeBytes" : 48000000,
        "maxWriteBatchSize" : 1000,
        "localTime" : ISODate("2016-08-30T05:42:12.328Z"),
        "maxWireVersion" : 3,
        "minWireVersion" : 0,
        "ok" : 1
}

//连接到primary或者secondary
# mongo localhost:27000
# mongo localhost:27001
# mongo localhost:27002

//在主复制集上插入文档
repSetTest:PRIMARY> db.replTest.insert({_id:1, value:'abc'})
WriteResult({ "nInserted" : 1 })

repSetTest:PRIMARY> db.replTest.findOne()
{ "_id" : 1, "value" : "abc" }


//连接到从库查询,提示not master
# mongo localhost:27001
MongoDB shell version: 3.0.12
connecting to: localhost:27001/test

repSetTest:SECONDARY> db.replTest.find()
Error: error: { "$err" : "not master and slaveOk=false", "code" : 13435 }

//开启slave查询
repSetTest:SECONDARY> rs.slaveOk(true)    
repSetTest:SECONDARY> db.replTest.find()
{ "_id" : 1, "value" : "abc" }

//辅助复制集不支持CUD
repSetTest:SECONDARY> db.replTest.insert({_id:2,value:"cde"})
WriteResult({ "writeError" : { "code" : undefined, "errmsg" : "not master" } })

三、复制集自动故障转移

# netstat -nltp|grep 27000
tcp        0      0 0.0.0.0:27000               0.0.0.0:*                   LISTEN      13555/mongod        

# kill -9 13555
# mongo localhost:27000
connecting to: localhost:27000/test
2016-08-30T13:44:55.671+0800 W NETWORK  Failed to connect to 127.0.0.1:27000,
 reason: errno:111 Connection refused
2016-08-30T13:44:55.672+0800 E QUERY    Error: couldn't connect to server localhost:27000 (127.0.0.1), 
connection attempt failed
    at connect (src/mongo/shell/mongo.js:181:14)
    at (connect):1:6 at src/mongo/shell/mongo.js:181
exception: connect failed

//连接到27001端口,如下面的查询,27000连接失败,27001已经提升为PRIMARY
# mongo localhost:27001
MongoDB shell version: 3.0.12
connecting to: localhost:27001/test
repSetTest:PRIMARY> rs.status()
{
        "set" : "repSetTest",
        "date" : ISODate("2016-08-30T05:45:39.018Z"),
        "myState" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:27000",
                        "health" : 0,
                        "state" : 8,
                        "stateStr" : "(not reachable/healthy)", //此时提示27000不可达
                        "uptime" : 0,
                        "optime" : Timestamp(0, 0),
                        "optimeDate" : ISODate("1970-01-01T00:00:00Z"),
                        "lastHeartbeat" : ISODate("2016-08-30T05:45:38.378Z"),
                        "lastHeartbeatRecv" : ISODate("2016-08-30T05:44:48.263Z"),
                        "pingMs" : 0,
                        "lastHeartbeatMessage" : "Failed attempt to connect to localhost:27000; 
                        couldn't connect to server localhost:27000 (127.0.0.1), connection attempt failed",
                        "configVersion" : -1
                },
                {
                        "_id" : 1,
                        "name" : "localhost:27001",   // Author : Leshami
                        "health" : 1,                 // Blog   : http://blog.csdn.net/leshami
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 372,
                        "optime" : Timestamp(1472535845, 2),
                        "optimeDate" : ISODate("2016-08-30T05:44:05Z"),
                        "electionTime" : Timestamp(1472535890, 1),
                        "electionDate" : ISODate("2016-08-30T05:44:50Z"),
                        "configVersion" : 1,
                        "self" : true
                },
                {
                        "_id" : 2,
                        "name" : "localhost:27002",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 272,
                        "optime" : Timestamp(1472535845, 2),
                        "optimeDate" : ISODate("2016-08-30T05:44:05Z"),
                        "lastHeartbeat" : ISODate("2016-08-30T05:45:38.356Z"),
                        "lastHeartbeatRecv" : ISODate("2016-08-30T05:45:38.356Z"),
                        "pingMs" : 0,
                        "configVersion" : 1
                }
        ],
        "ok" : 1
}

//重新启动27000实例
# mongod --replSet repSetTest --dbpath /data/n1 --logpath /data/n1/n1.log --port \
> 27000 --smallfiles --oplogSize 128 --fork
about to fork child process, waiting until server is ready for connections.
forked process: 16473
child process started successfully, parent exiting

//再次查看复制集的状态,此时27000为辅助副本
repSetTest:PRIMARY> rs.status()
{
        "set" : "repSetTest",
        "date" : ISODate("2016-08-30T05:47:25.220Z"),
        "myState" : 1,
        "members" : [
                {
                        "_id" : 0,
                        "name" : "localhost:27000",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",  //此时该节点变成了辅助节点
                        "uptime" : 12,
                        "optime" : Timestamp(1472535845, 2),
                        "optimeDate" : ISODate("2016-08-30T05:44:05Z"),
                        "lastHeartbeat" : ISODate("2016-08-30T05:47:24.819Z"),
                        "lastHeartbeatRecv" : ISODate("2016-08-30T05:47:25.061Z"),
                        "pingMs" : 0,
                        "configVersion" : 1
                },
                {
                        "_id" : 1,
                        "name" : "localhost:27001",
                        "health" : 1,
                        "state" : 1,
                        "stateStr" : "PRIMARY",
                        "uptime" : 478,
                        "optime" : Timestamp(1472535845, 2),
                        "optimeDate" : ISODate("2016-08-30T05:44:05Z"),
                        "electionTime" : Timestamp(1472535890, 1),
                        "electionDate" : ISODate("2016-08-30T05:44:50Z"),
                        "configVersion" : 1,
                        "self" : true
                },
                {
                        "_id" : 2,
                        "name" : "localhost:27002",
                        "health" : 1,
                        "state" : 2,
                        "stateStr" : "SECONDARY",
                        "uptime" : 379,
                        "optime" : Timestamp(1472535845, 2),
                        "optimeDate" : ISODate("2016-08-30T05:44:05Z"),
                        "lastHeartbeat" : ISODate("2016-08-30T05:47:24.816Z"),
                        "lastHeartbeatRecv" : ISODate("2016-08-30T05:47:24.816Z"),
                        "pingMs" : 0,
                        "configVersion" : 1
                }
        ],
        "ok" : 1
}

四、获取复制集的帮助

repSetTest:PRIMARY> rs.help()  //获取副本集相关的帮助命令
    rs.status()                        { replSetGetStatus : 1 } checks repl set status
    rs.initiate()                      { replSetInitiate : null } initiates set with default settings
    rs.initiate(cfg)                   { replSetInitiate : cfg } initiates set with configuration cfg
    rs.conf()                          get the current configuration object from local.system.replset
    rs.reconfig(cfg)                   updates the configuration of a running replica set with cfg (disconnects)
    rs.add(hostportstr)                add a new member to the set with default attributes (disconnects)
    rs.add(membercfgobj)               add a new member to the set with extra attributes (disconnects)
    rs.addArb(hostportstr)             add a new member which is arbiterOnly:true (disconnects)
    rs.stepDown([stepdownSecs, catchUpSecs])   step down as primary (disconnects)
    rs.syncFrom(hostportstr)                   make a secondary sync from the given member
    rs.freeze(secs)                            make a node ineligible to become primary for the time specified
    rs.remove(hostportstr)                     remove a host from the replica set (disconnects)
    rs.slaveOk()                               allow queries on secondary nodes

    rs.printReplicationInfo()                  check oplog size and time range
    rs.printSlaveReplicationInfo()             check replica set members and replication lag
    db.isMaster()                              check who is primary

    reconfiguration helpers disconnect from the database so the shell will display
    an error, even if the command succeeds.
相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
8月前
|
NoSQL MongoDB
MongoDB-复制集搭建
在安装目录下新建 data/conf/log 3个文件夹,在conf文件夹下新建 mongo.config,在mongo.config中配置如下内容:
59 0
|
2月前
|
NoSQL Linux MongoDB
centos7搭建MongoDB以及MongoDB复制集
centos7搭建MongoDB以及MongoDB复制集
36 0
|
8月前
|
数据库
MongoDB-复制集投票节点
?> 投票节点就是不保存任何数据, 只参与投票的节点
49 0
|
5月前
|
监控 NoSQL MongoDB
轻松掌握组件启动之MongoDB(番外篇):高可用复制集架构环境搭建-mtools
mtools是一个基于Python实现的MongoDB工具集,旨在提供一系列功能,包括MongoDB日志分析、报表生成以及简易的数据库安装等。它由MongoDB原生的工程师单独发起并进行开源维护。mtools包含了一些常用的组件,如mlaunch、mlogfilter、mplotqueries和mlogvis等,可以帮助我们更方便地启动和创建MongoDB数据库。
|
5月前
|
监控 NoSQL 安全
轻松掌握组件启动之MongoDB(下):高可用复制集架构环境搭建
本章介绍了MongoDB复制集的配置和使用方法,如何初始化和添加节点到复制集,验证主节点的写入和从节点的读取功能。了解如何查询复制集的状态,包括成员的健康状况、同步信息和角色等。最后,我们介绍了如何配置复制集的安全认证,包括创建用户和生成keyFile文件,并演示了使用认证信息连接复制集的方式。通过本章的学习,你将掌握MongoDB复制集的基本使用和配置方法。
|
5月前
|
存储 NoSQL 容灾
轻松掌握组件启动之MongoDB(上):高可用复制集架构环境搭建
本文介绍了MongoDB复制集的架构和特点,强调了使用复制集提供数据的高可用性和冗余性的重要性。复制集由Primary节点和Secondary节点组成,确保数据一致性。复制集还具有数据分发、读写分离和异地容灾等附加功能。使用MongoDB复制集可以提供稳定可靠的数据存储和高可用性。
轻松掌握组件启动之MongoDB(上):高可用复制集架构环境搭建
|
8月前
|
数据库 索引
MongoDB-复制集同步规则
初始化同步 • 将一个新的节点加入到复制集中时, 就需要进行初始化同步 • 初始化同步会先清空自己所有的内容, 保证将来自己和主节点一模一样 • 初始化同步会将主节点中现有所有的 ‘数据库’, ‘集合’, ‘文档’, ‘索引’ 全部拷贝过来 • 但是在拷贝的过程中主节点仍然可能会做一些其它操作, 新增一些其它的数据等
55 0
|
1天前
|
NoSQL MongoDB 数据库
MongoDB数据恢复—MongoDB数据库文件被破坏的数据恢复案例
服务器数据恢复环境: 一台Windows Server操作系统服务器,服务器上部署MongoDB数据库。 MongoDB数据库故障&检测: 工作人员在未关闭MongoDB数据库服务的情况下,将数据库文件拷贝到其他分区。拷贝完成后将原MongoDB数据库所在分区进行了格式化操作,然后将数据库文件拷回原分区,重新启动MongoDB服务,服务无法启动。
|
4天前
|
NoSQL MongoDB Redis
Python与NoSQL数据库(MongoDB、Redis等)面试问答
【4月更文挑战第16天】本文探讨了Python与NoSQL数据库(如MongoDB、Redis)在面试中的常见问题,包括连接与操作数据库、错误处理、高级特性和缓存策略。重点介绍了使用`pymongo`和`redis`库进行CRUD操作、异常捕获以及数据一致性管理。通过理解这些问题、易错点及避免策略,并结合代码示例,开发者能在面试中展现其技术实力和实践经验。
36 8
Python与NoSQL数据库(MongoDB、Redis等)面试问答
|
1月前
|
NoSQL 网络协议 MongoDB
Windows公网远程连接MongoDB数据库【无公网IP】
Windows公网远程连接MongoDB数据库【无公网IP】