搭建高可用MongoDB集群——副本集部署

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
简介:

一、副本集(repl set)简介


    早期版本使用 master-slave 模式,一主一从和 MySQL 类似,但 slave 在此架构中为只读,当主库宕机后,从库不能自动切换为主。目前已经淘汰了 master-slave 模式,改为副本集,这种模式下有一个主(primary),和多个从(secondary)只读。支持给它们设置权重,当主宕掉后,权重最高的切换为主。

    在此架构中还可以建立一个仲裁(arbiter)的角色,它只负责裁决,而不存储数据。并且此架构读写数据都在主上,优点在于能够自动的切换角色,但是缺点在于不能实现让用户的访问由原来的主自动切换到现在的主,这个还需手动切换来实现负载均衡的目的;当然这个我们可以写一个监控脚本,让程序自动的帮我们切换到要访问的主的IP即可。

    最后应该注意一点,要想使用副本集,从的mongodb的数据必须都是空的,要不然执行  rs.initiate()命令时会提示因存在数据而导致初始化不成功(has data already, cannot initiate set)。

 


二、副本集的架构


1、主要流程

wKiom1ZoBZ2wO-JeAAGsLXU9Hqo857.png

2、裁决机制

wKiom1ZoBzuTazhQAALKnyTvjg0240.png

三、副本集的搭建


1、准备工作

准备好以下三台机器

主(primary):192.168.0.103

从(secondary1):192.168.0.109

从(secondary2):192.168.0.120

这三台机器全部都安装好 MongoDB ,安装的详细过程参考我上篇文章MongoDB的安装:

http://msiyuetian.blog.51cto.com/8637744/1720559


2、编辑配置文件

1)对三台机器的配置文件都进行以下修改

# vim /etc/mongod.conf

找到:

#replication:

修改为:

replication:

  oplogSizeMB: 20    

  replSetName: tpp

说明:oplogSizeMB 指 oplog 大小;replSetName 指副本集名称。注意格式是前面空两格,冒号后空一格。


2)然后重启三台 MongoDB

# /etc/init.d/mongod restart

若重启失败,用 mongod -f /etc/mongod.conf 命令开启。


3)连接主

[root@primary ~]# mongo

MongoDB shell version: 3.0.7

connecting to: test

> use admin

switched to db admin

> config={_id:"tpp",members:[{_id:0,host:"192.168.0.103:27017"},{_id:1,host:"192.168.0.109:27017"},{_id:2,host:"192.168.0.120:27017"}]}

{

        "_id" : "tpp",

        "members" : [

                {

                        "_id" : 0,

                        "host" : "192.168.0.103:27017"

                },

                {

                        "_id" : 1,

                        "host" : "192.168.0.109:27017"

                },

                {

                        "_id" : 2,

                        "host" : "192.168.0.120:27017"

                }

        ]

}

> rs.initiate(config)                                  //初始化

{ "ok" : 1 }

tpp:OTHER> rs.status()                             //查看状态

{

        "set" : "tpp",

        "date" : ISODate("2015-12-12T23:19:04.253Z"),

        "myState" : 1,

        "members" : [

                {

                        "_id" : 0,

                        "name" : "192.168.0.103:27017",

                        "health" : 1,

                        "state" : 1,

                        "stateStr" : "PRIMARY",

                        "uptime" : 10034,

                        "optime" : Timestamp(1449961495, 1),

                        "optimeDate" : ISODate("2015-12-12T23:04:55Z"),

                        "electionTime" : Timestamp(1449961497, 1),

                        "electionDate" : ISODate("2015-12-12T23:04:57Z"),

                        "configVersion" : 1,

                        "self" : true

                },

                {

                        "_id" : 1,

                        "name" : "192.168.0.109:27017",

                        "health" : 1,

                        "state" : 2,

                        "stateStr" : "SECONDARY",

                        "uptime" : 848,

                        "optime" : Timestamp(1449961495, 1),

                        "optimeDate" : ISODate("2015-12-12T23:04:55Z"),

                        "lastHeartbeat" : ISODate("2015-12-12T23:19:04.073Z"),

                        "lastHeartbeatRecv" : ISODate("2015-12-12T23:19:03.796Z"),

                        "pingMs" : 0,

                        "configVersion" : 1

                },

                {

                        "_id" : 2,

                        "name" : "192.168.0.120:27017",

                        "health" : 1,

                        "state" : 2,

                        "stateStr" : "SECONDARY",

                        "uptime" : 848,

                        "optime" : Timestamp(1449961495, 1),

                        "optimeDate" : ISODate("2015-12-12T23:04:55Z"),

                        "lastHeartbeat" : ISODate("2015-12-12T23:19:04.073Z"),

                        "lastHeartbeatRecv" : ISODate("2015-12-12T23:19:03.831Z"),

                        "pingMs" : 0,

                        "configVersion" : 1

                }

        ],

        "ok" : 1

}

注意:两个从上的状态为 "stateStr" : "SECONDARY",若为 "STARTUP",则需要进行如下操作:

> var config={_id:"tpp",members:[{_id:0,host:"192.168.0.103:27017"},{_id:1,host:"192.168.0.109:27017"},{_id:2,host:"192.168.0.120:27017"}]}

> rs.initiate(config)

查看到以上信息就说明配置成功了,我们也会发现主上前缀变为 tpp:PRIMARY

tpp:PRIMARY>

而两个从上都变为 tpp:SECONDARY

[root@secondary1 ~]# mongo

MongoDB shell version: 3.0.7

connecting to: test

tpp:SECONDARY>


三、副本集的测试


测试1:同步数据测试

1)主上建库、建集合

tpp:PRIMARY> use mydb

switched to db mydb

tpp:PRIMARY> db.createCollection('test1')

{ "ok" : 1 }

tpp:PRIMARY> show dbs

local  0.078GB

mydb   0.078GB

2)从上查看

tpp:SECONDARY> show dbs                                 //会报错,因为secondary节点默认不可读

2015-12-13T07:44:46.256+0800 E QUERY    Error: listDatabases failed:{ "note" : "from execCommand", "ok" : 0, "errmsg" : "not master" }

    at Error (<anonymous>)

    at Mongo.getDBs (src/mongo/shell/mongo.js:47:15)

    at shellHelper.show (src/mongo/shell/utils.js:630:33)

    at shellHelper (src/mongo/shell/utils.js:524:36)

    at (shellhelp2):1:1 at src/mongo/shell/mongo.js:47

tpp:SECONDARY> rs.slaveOk()                               //执行该命令后就可以查询了

tpp:SECONDARY> show dbs

local  0.078GB

mydb   0.078GB

注意:通过上面的命令只能临时查询,下次再通过mongo命令进入后查询仍会报错,所以可以修改文件

[root@secondary2 ~]# vi ~/.mongorc.js                  //写入下面这行

rs.slaveOk();

[root@secondary2 ~]# service mongod restart

[root@secondary2 ~]# mongo

MongoDB shell version: 3.0.7

connecting to: test

tpp:SECONDARY> show dbs

local  0.078GB

mydb   0.078GB


测试2:模拟主宕机,从变成主

1)在主上查看所有机器的权重

tpp:PRIMARY> rs.config()

{

        "_id" : "tpp",

        "version" : 1,

        "members" : [

                {

                        "_id" : 0,

                        "host" : "192.168.0.103:27017",

                        "arbiterOnly" : false,

                        "buildIndexes" : true,

                        "hidden" : false,

                        "priority" : 1,

                        "tags" : {


                        },

                        "slaveDelay" : 0,

                        "votes" : 1

                },

                {

                        "_id" : 1,

                        "host" : "192.168.0.109:27017",

                        "arbiterOnly" : false,

                        "buildIndexes" : true,

                        "hidden" : false,

                        "priority" : 1,

                        "tags" : {


                        },

                        "slaveDelay" : 0,

                        "votes" : 1

                },

                {

                        "_id" : 2,

                        "host" : "192.168.0.120:27017",

                        "arbiterOnly" : false,

                        "buildIndexes" : true,

                        "hidden" : false,

                        "priority" : 1,

                        "tags" : {


                        },

                        "slaveDelay" : 0,

                        "votes" : 1

                }

        ],

        "settings" : {

                "chainingAllowed" : true,

                "heartbeatTimeoutSecs" : 10,

                "getLastErrorModes" : {


                },

                "getLastErrorDefaults" : {

                        "w" : 1,

                        "wtimeout" : 0

                }

        }

}

注意:默认所有的机器权重都为1,如果任何一个权重设置为比其他的高,则该台机器立马会切换为primary角色,所以要我们预设三台机器的权重。


2)设置权重

预设主(0.103)权重为3、从(0.109)权重为2、从(0.120)权重为1(默认值可不用重新赋值)。

在主上执行下面命令:

tpp:PRIMARY> cfg=rs.config()                            //重新赋值

tpp:PRIMARY> cfg.members[0].priority = 3

3

tpp:PRIMARY> cfg.members[1].priority = 2

2

tpp:PRIMARY> rs.reconfig(cfg)                           //重新加载配置

{ "ok" : 1 }

tpp:PRIMARY> rs.config()                                   //重新查看权重已改变

{

        "_id" : "tpp",

        "version" : 2,

        "members" : [

                {

                        "_id" : 0,

                        "host" : "192.168.0.103:27017",

                        "arbiterOnly" : false,

                        "buildIndexes" : true,

                        "hidden" : false,

                        "priority" : 3,

                        "tags" : {


                        },

                        "slaveDelay" : 0,

                        "votes" : 1

                },

                {

                        "_id" : 1,

                        "host" : "192.168.0.109:27017",

                        "arbiterOnly" : false,

                        "buildIndexes" : true,

                        "hidden" : false,

                        "priority" : 2,

                        "tags" : {


                        },

                        "slaveDelay" : 0,

                        "votes" : 1

                },

                {

                        "_id" : 2,

                        "host" : "192.168.0.120:27017",

                        "arbiterOnly" : false,

                        "buildIndexes" : true,

                        "hidden" : false,

                        "priority" : 1,

                        "tags" : {


                        },

                        "slaveDelay" : 0,

                        "votes" : 1

                }

        ],

        "settings" : {

                "chainingAllowed" : true,

                "heartbeatTimeoutSecs" : 10,

                "getLastErrorModes" : {


                },

                "getLastErrorDefaults" : {

                        "w" : 1,

                        "wtimeout" : 0

                }

        }

}

3)禁掉主上的mongod服务,模拟主宕机

[root@primary ~]# iptables -I INPUT -p tcp --dport 27017 -j DROP

执行上面命令以后,在权重大的从(0.109)上敲下回车,从的角色立马由SECONDARY变为PRIMARY:

tpp:SECONDARY>

tpp:PRIMARY>

tpp:PRIMARY> rs.status()                          //查看状态

{

        "set" : "tpp",

        "date" : ISODate("2015-12-13T00:29:11.161Z"),

        "myState" : 1,

        "members" : [

                {

                        "_id" : 0,

                        "name" : "192.168.0.103:27017",

                        "health" : 0,

                        "state" : 8,

                        "stateStr" : "(not reachable/healthy)",

                        "uptime" : 0,

                        "optime" : Timestamp(0, 0),

                        "optimeDate" : ISODate("1970-01-01T00:00:00Z"),

                        "lastHeartbeat" : ISODate("2015-12-13T00:29:07.659Z"),

                        "lastHeartbeatRecv" : ISODate("2015-12-13T00:29:09.708Z"),

                        "pingMs" : 0,

                        "lastHeartbeatMessage" : "Failed attempt to connect to 192.168.0.103:27017; couldn't connect to server 192.168.0.103:27017 (192.168.0.103), connection attempt failed",

                        "configVersion" : -1

                },

                {

                        "_id" : 1,

                        "name" : "192.168.0.109:27017",

                        "health" : 1,

                        "state" : 1,

                        "stateStr" : "PRIMARY",

                        "uptime" : 5165,

                        "optime" : Timestamp(1449965758, 1),

                        "optimeDate" : ISODate("2015-12-13T00:15:58Z"),

                        "electionTime" : Timestamp(1449966238, 1),

                        "electionDate" : ISODate("2015-12-13T00:23:58Z"),

                        "configVersion" : 2,

                        "self" : true

                },

                {

                        "_id" : 2,

                        "name" : "192.168.0.120:27017",

                        "health" : 1,

                        "state" : 2,

                        "stateStr" : "SECONDARY",

                        "uptime" : 5053,

                        "optime" : Timestamp(1449965758, 1),

                        "optimeDate" : ISODate("2015-12-13T00:15:58Z"),

                        "lastHeartbeat" : ISODate("2015-12-13T00:29:09.922Z"),

                        "lastHeartbeatRecv" : ISODate("2015-12-13T00:29:09.980Z"),

                        "pingMs" : 0,

                        "configVersion" : 2

                }

        ],

        "ok" : 1

}

注意:由上面可发现原来的主(0.103)已处于不健康状态(失连),而原来权重高的从(0.109)的角色变为PRIMARY,权重低的从角色不变。不过要想让用户识别新主,还需手动指定读库的目标server。


测试3:原主恢复,新主写入的数据是否同步

1)新主建库、建集合

tpp:PRIMARY> use mydb2

switched to db mydb2

tpp:PRIMARY> db.createCollection('test2')

{ "ok" : 1 }

tpp:PRIMARY> show dbs

local  0.078GB

mydb   0.078GB

mydb2  0.078GB

2)删除规则,恢复原主

[root@primary ~]# iptables -D INPUT -p tcp --dport 27017 -j DROP      

新主上按下回车,新主角色由 PRIMARY 变为 SECONDARY,并输出如下信息:

tpp:PRIMARY>

2015-12-13T08:58:37.143+0800 I NETWORK  DBClientCursor::init call() failed

2015-12-13T08:58:37.145+0800 I NETWORK  trying reconnect to 127.0.0.1:27017 (127.0.0.1) failed

2015-12-13T08:58:37.146+0800 I NETWORK  reconnect 127.0.0.1:27017 (127.0.0.1) ok

tpp:SECONDARY>



3)登入原主,查看数据库信息


[root@primary ~]# mongo

MongoDB shell version: 3.0.7

connecting to: test

tpp:PRIMARY> show dbs

local  0.078GB

mydb   0.078GB

mydb2  0.078GB

说明:由上面可知,当权重更高的原主恢复运行了,在新主期间写入的新数据同样同步到了原主上,即副本集成功实现了负载均衡的目的。








      本文转自 M四月天 51CTO博客,原文链接:http://blog.51cto.com/msiyuetian/1722406,如需转载请自行联系原作者




相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。 &nbsp; 相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
2月前
|
NoSQL 容灾 MongoDB
MongoDB主备副本集方案:两台服务器使用非对称部署的方式实现高可用与容灾备份
在资源受限的情况下,为了实现MongoDB的高可用性,本文探讨了两种在两台服务器上部署MongoDB的方案。方案一是通过主备身份轮换,即一台服务器作为主节点,另一台同时部署备节点和仲裁节点;方案二是利用`priority`设置实现自动主备切换。两者相比,方案二自动化程度更高,适合追求快速故障恢复的场景,而方案一则提供了更多的手动控制选项。文章最后对比了这两种方案与标准三节点副本集的优缺点,指出三节点方案在高可用性和数据一致性方面表现更佳。
128 5
|
3月前
|
存储 NoSQL MongoDB
MongoDB 复制(副本集)
10月更文挑战第17天
54 2
MongoDB 复制(副本集)
|
2月前
|
存储 NoSQL MongoDB
【赵渝强老师】部署MongoDB复制集
本文介绍了如何在单个节点上搭建MongoDB复制集环境,通过监听不同端口实现多节点配置。详细步骤包括创建数据目录、编辑配置文件、启动节点、初始化复制集、查看状态以及测试主从库的读写操作。文中还提供了视频讲解和代码示例,帮助读者更好地理解和操作。
|
4月前
|
存储 NoSQL Shell
MongoDB复制(副本集)总结
这篇文章是关于MongoDB副本集的总结,包括复制原理、设置副本集、案例分析等内容。
56 1
|
5月前
|
存储 NoSQL MongoDB
今日分享MongoDB一键部署脚本
今日分享MongoDB一键部署脚本
49 0
|
5月前
|
存储 运维 NoSQL
轻松上手:逐步搭建你的高可用MongoDB集群(分片)
【8月更文挑战第13天】在数据激增的背景下,传统单机数据库难以胜任。MongoDB作为流行NoSQL数据库,采用分片技术实现水平扩展,有效处理海量数据。分片将数据分散存储,提高并发处理能力和容错性,是高可用架构基石。构建MongoDB集群需理解shard、config server和router三组件协同工作原理。通过具体实例演示集群搭建流程,包括各组件的启动及配置,确保数据高可用性和系统稳定性。合理规划与实践可构建高效稳定的MongoDB集群,满足业务需求并支持未来扩展。
155 0
|
24天前
|
存储 JSON NoSQL
学习 MongoDB:打开强大的数据库技术大门
MongoDB 是一个基于分布式文件存储的文档数据库,由 C++ 编写,旨在为 Web 应用提供可扩展的高性能数据存储解决方案。它与 MySQL 类似,但使用文档结构而非表结构。核心概念包括:数据库(Database)、集合(Collection)、文档(Document)和字段(Field)。MongoDB 使用 BSON 格式存储数据,支持多种数据类型,如字符串、整数、数组等,并通过二进制编码实现高效存储和传输。BSON 文档结构类似 JSON,但更紧凑,适合网络传输。
64 15
|
1月前
|
存储 NoSQL 关系型数据库
阿里云数据库MongoDB版助力信也科技 打造互联网金融企业样板
我们的风控系统引入阿里云数据库MongoDB版后,解决了特征类字段灵活加减的问题,大大提高了开发效率,极大的提升了业务用户体验,获得了非常好的效果
阿里云数据库MongoDB版助力信也科技 打造互联网金融企业样板
|
2月前
|
NoSQL Cloud Native atlas
探索云原生数据库:MongoDB Atlas 的实践与思考
【10月更文挑战第21天】本文探讨了MongoDB Atlas的核心特性、实践应用及对云原生数据库未来的思考。MongoDB Atlas作为MongoDB的云原生版本,提供全球分布式、完全托管、弹性伸缩和安全合规等优势,支持快速部署、数据全球化、自动化运维和灵活定价。文章还讨论了云原生数据库的未来趋势,如架构灵活性、智能化运维和混合云支持,并分享了实施MongoDB Atlas的最佳实践。