MongoDB添加secondary节点的2种方法详解

前言

前段时间维护的一个事业群的其中一条业务线的开发找到运维,提出来了一个MongoDB的优化问题,那段时间MongoDB正在从op管理移交给db进行维护,整个部门都对MongoDB的运维经验缺乏,MongoDB的优化更是一个未知的挑战。当op找到我,核心系统的公共服务平台用来进行短信服务的MongoDB集群想进行一次优化,我当仁不能让的承担了这项我都觉得可能搞不定的任务。

开发找到我提出了两点儿问题,并寻求运维团队解决这个问题,不过最终在我的理性的思考和他感性的思维碰撞下,最终我还是以胜利者的姿态胜出。我成功说服了他,并解答了他一些疑问,得到了满意的答复后再也没找我了。当然这里肯定不会就凭几句话,任你理论再怎么丰富,态度如何暧昧,不拿点儿真实数据,做点儿什么,怎么能说服经验丰富的开发认定的事儿。沟通了大半天,占据了我白天的工作时间,不过他提出来的问题还是很值得讨论。

根据开发的逻辑,是想横向扩充secondary节点,把其他要求不高的业务放到secondary节点上,减轻primary节点的压力,达到部分读写分离,使得主要业务优先保障。我觉得这个出发点是好的,但并没有就此作出回应,其一是他没有认识到这个他认为的有延迟并不是数据库集群的问题(这里不详细讲述排查的过程,下一篇文章会讲些MongoDB的写入与业务逻辑),其二是我们确实缺乏有效的资源硬件去进行扩充节点。

不同的业务场景应用不同的架构策略,扩充secondary节点有时候不能解决问题,尤其是那些实时性很高的业务,但有时候扩充secondary节点确实有效,比如硬件升级后需要做的服务迁移,需要在线扩充secondary节点来满足业务需要的更高的硬件要求。

MongoDB的secondary节点的扩充,我总结起来有两种方式:

1、rs.add()直接扩充

2、一致性备份后进行扩充(个人叫法)

1、rs.add("HOST_NAME:PORT")

具体的实现方式是登陆扩充节点的机器,编辑好配置文件,并建立相应的目录和权限,启动MongoDB实例就可以了。

需要注意的一点儿是这种扩充方式要保证同步源的数据量级,即保证在同步完数据前MongoDB的oplog不会被覆盖,这点儿类似与MySQL的redo log日志,如果被覆盖那么同步的数据出现不一致,导致同步失败。

需要注意的另一点是同步数据的过程中,当集群数据达到一定量级时,同步数据的大小很大就会对网络造成一定的压力,可能对业务的核心交换机造成影响,因此需要用TC工具对同步流量做限速处理。这个限速需要考虑同步源可能不会是primary,也可能是同样角色的secondary节点,令外限速同步势必会增大同步时间,这个会增大oplog被覆盖的概率,具体限速值还是要经过计算才能把握好。

2、一致性快照快速添加secondary节点(自我命名,欢迎各位交流)

  a)primary节点上进行一致性快照备份

  b)secondary节点上进行一致性快照恢复,仅仅对数据部分进行恢复,暂时不要对oplog进行恢复

c)初始化oplog.rs集合,并恢复oplog记录

d)初始化local数据库的其他两个集合db.replset.election,db.system.replset

  e)修改数据库配置并重启数据库(这一步操作前实例不开启认证模式、复制集的配置),rs.add("HOST_NAME:PORT")将secondary添加进集群并观察同步状态、校验数据的完整和一致性

实践的详细实践过程如下(仅供参考交流,生产环境慎用):

1、primary上进行一致性快照备份

#primary节点或者其他secondary节点备份数据
[root@172-16-3-190 mongodb]# /opt/app/mongodb/bin/mongodump -uroot -ppwd4mysql --authenticationDatabase=admin --port=27017 --oplog -o /tmp/dump_mongo/
2018-08-20T15:42:47.028+0800 writing admin.system.users to
2018-08-20T15:42:47.030+0800 done dumping admin.system.users (1 document)
2018-08-20T15:42:47.030+0800 writing admin.system.version to
2018-08-20T15:42:47.031+0800 done dumping admin.system.version (2 documents)
2018-08-20T15:42:47.032+0800 writing super_hero.user_address to
2018-08-20T15:42:47.032+0800 writing super_hero.user_info to
2018-08-20T15:42:47.033+0800 done dumping super_hero.user_address (1 document)
2018-08-20T15:42:47.033+0800 done dumping super_hero.user_info (1 document)
2018-08-20T15:42:47.034+0800 writing captured oplog to
2018-08-20T15:42:47.036+0800 dumped 1 oplog entry

#查看备份的文件
[root@172-16-3-190 mongodb]# ls -lh /tmp/dump_mongo/
total 12K
drwxr-xr-x 2 root root 4.0K Aug 20 15:42 admin
-rw-r--r-- 1 root root 110 Aug 20 15:42 oplog.bson
drwxr-xr-x 2 root root 4.0K Aug 20 15:42 super_hero

#传递备份到准备添加为secondary的节点上
[root@172-16-3-190 tmp]# scp -r -P22222 /tmp/dump_mongo/ liyingxiao@172.16.3.189:/tmp

2、secondary节点一致性快照恢复

#auth=true
#replSet = repl_mongo
#clusterAuthMode=keyFile
#keyFile=/opt/app/mongodb/keyfile/mongodb.key

##恢复数据
[root@172-16-3-189 we_ops_admin]# /opt/app/mongodb/bin/mongorestore --oplogReplay --port=27017 /tmp/dump_mongo/
2018-08-20T15:56:32.161+0800 preparing collections to restore from
2018-08-20T15:56:32.193+0800 reading metadata for super_hero.user_info from /tmp/dump_mongo/super_hero/user_info.metadata.json
2018-08-20T15:56:32.194+0800 reading metadata for super_hero.user_address from /tmp/dump_mongo/super_hero/user_address.metadata.json
2018-08-20T15:56:32.222+0800 restoring super_hero.user_address from /tmp/dump_mongo/super_hero/user_address.bson
2018-08-20T15:56:32.300+0800 restoring super_hero.user_info from /tmp/dump_mongo/super_hero/user_info.bson
2018-08-20T15:56:32.867+0800 no indexes to restore
2018-08-20T15:56:32.867+0800 finished restoring super_hero.user_address (1 document)
2018-08-20T15:56:32.881+0800 no indexes to restore
2018-08-20T15:56:32.881+0800 finished restoring super_hero.user_info (1 document)
2018-08-20T15:56:32.881+0800 restoring users from /tmp/dump_mongo/admin/system.users.bson
2018-08-20T15:56:32.993+0800 replaying oplog
2018-08-20T15:56:32.997+0800 done

3、初始化oplog.rs集合,并恢复oplog记录

创建oplog.rs集合并初始化大小

 use local
 db.createCollection("oplog.rs",{"capped":true,"size":100000000})

恢复一致性备份的oplog.rs集合的数据到secondary节点

[root@172-16-3-189 we_ops_admin]# /opt/app/mongodb/bin/mongorestore -d local -c oplog.rs --port=27017 /tmp/dump_mongo/oplog.bson
2018-08-20T16:12:49.848+0800 checking for collection data in /tmp/dump_mongo/oplog.bson
2018-08-20T16:12:49.852+0800 restoring local.oplog.rs from /tmp/dump_mongo/oplog.bson
2018-08-20T16:12:49.925+0800 no indexes to restore
2018-08-20T16:12:49.925+0800 finished restoring local.oplog.rs (1 document)
2018-08-20T16:12:49.925+0800 done

4、初始化db.replset.election,db.system.replset集合,其中replset.election需要查询主节点数据并将这些数据存储到secondary节点,或者两个结合自行save到secondary节点。另集合system.replset加入复制集后可自动识别primary节点内容(这里我采取自行同步数据)

 #primary节点
 repl_mongo:PRIMARY> db.replset.election.find()
 { "_id" : ObjectId("5b7a6ee5de7a24b82a686139"), "term" : NumberLong(1), "candidateIndex" : NumberLong(0) }
 #secondary节点
 db.replset.election.save({ "_id" : ObjectId("5b7a6ee5de7a24b82a686139"), "term" : NumberLong(1), "candidateIndex" : NumberLong(0) })

5、修改数据库配置并重启,添加secondary节点到复制集群中

#auth=true
#replSet = repl_mongo
#clusterAuthMode=keyFile
#keyFile=/opt/app/mongodb/keyfile/mongodb.key

[root@172-16-3-189 we_ops_admin]# /opt/app/mongodb/bin/mongod --shutdown -f /opt/app/mongodb/mongo.conf
killing process with pid: 5331
[root@172-16-3-189 we_ops_admin]# vim /opt/app/mongodb/mongo.conf #注释去掉并重启
[root@172-16-3-189 we_ops_admin]# /opt/app/mongodb/bin/mongod -f /opt/app/mongodb/mongo.conf
about to fork child process, waiting until server is ready for connections.
forked process: 5722
child process started successfully, parent exiting

#添加secondary节点
repl_mongo:PRIMARY> rs.add({"_id":1,"host":"172.16.3.189:27017"})
{
  "ok" : 1,
  "operationTime" : Timestamp(1534752953, 1),
  "$clusterTime" : {
    "clusterTime" : Timestamp(1534752953, 1),
    "signature" : {
      "hash" : BinData(0,"Tt9nzhoVYdUtGFZnc1Kg1exl0Hc="),
      "keyId" : NumberLong("6591702943026642945")
    }
  }
}

6、登录添加的secondary节点,验证复制集状态,数据完整和一致性。

 [root@172-16-3-189 we_ops_admin]# /opt/app/mongodb/bin/mongo -uroot -ppwd4mysql --authenticationDatabase=admin --port=27017

重点介绍第二种省时省心但费力费操作的添加secondary节点的方法,实践过程中数据库实例前期去掉认证和复制集参数,是方便我们下面的一些需要用户权限的操作,避免建立管理员账号,后续加入集群后自行同步了primary节点的账号。重启后登录secondary节点验证服务的可用性和数据一致性时,使用集群的管理账号进入,否则会报认证的错误。

总结如上两种扩充方式,对于方式1的扩充简单省事,需要保证oplog不被覆盖和评估同步流量的影响问题,是我们通常进行横向复制集添加secondary节点的方法。对于第二种方式,操作繁琐但不用担心oplog被覆盖,且操作期间不会过多担忧网络流量的问题,仅仅考虑网络传输的流量影响。第一种方式操作时间周期长,不可控的影响范围大费时费精力,第二种方式操作时间短,操作的步骤多,容易出现其他问题。

MongoDB secondary节点出现recovering状态

MongoDB做了replica sets之后,secondary节点出现recovering状态

在一次mongo集群挂掉后,重启,发现有一台服务器的mongo节点一直处于recovering状态,不能变为secondary或者primary。

查询官方文档后,找到解决方案,在此记录。

出现原因

备份节点的工作原理过程可以大致描述为,备份节点定期轮询主节点上的数据操作,然后对自己的数据副本进行这些操作,从而保证跟主节点的数据同步。

至于主节点上的所有数据库状态改变的操作,都会存放在一张特定的系统表中。备份节点则是根据这些数据进行自己的数据更新。

上面提到的数据库状态改变的操作,称为oplog(operation log,主节点操作记录)。oplog存储在local数据库的"oplog.rs"表中。副本集中备份节点异步的从主节点同步oplog,然后重新执行它记录的操作,以此达到了数据同步的作用。

关于oplog有几个注意的地方:

  • oplog只记录改变数据库状态的操作
  • 存储在oplog中的操作并不是和主节点执行的操作完全一样,例如"$inc"操作就会转化为"$set"操作
  • oplog存储在固定集合中(capped collection),当oplog的数量超过oplogSize,新的操作就会覆盖旧的操作

数据同步

在副本集中,有两种数据同步方式:

  • initial sync(初始化):这个过程发生在当副本集中创建一个新的数据库或其中某个节点刚从宕机中恢复,或者向副本集中添加新的成员的时候,默认的,副本集中的节点会从离它最近的节点复制oplog来同步数据,这个最近的节点可以是primary也可以是拥有最新oplog副本的secondary节点。
  • 该操作一般会重新初始化备份节点,开销较大
  • replication(复制):在初始化后这个操作会一直持续的进行着,以保持各个secondary节点之间的数据同步。

initial sync

当遇到上面例子中无法同步的问题时,只能使用以下两种方式进行initial sync了

  • 第一种方式就是停止该节点,然后删除目录中的文件,重新启动该节点。这样,这个节点就会执行initial sync
    注意:通过这种方式,sync的时间是根据数据量大小的,如果数据量过大,sync时间就会很长
    同时会有很多网络传输,可能会影响其他节点的工作
  • 第二种方式,停止该节点,然后删除目录中的文件,找一个比较新的节点,然后把该节点目录中的文件拷贝到要sync的节点目录中

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • centos7防火墙导致java程序访问mongodb3.0.1时报错的问题分析

    环境描述: 数据库:mongodb3.0.1 数据库系统:centos7,(虚拟机,最小安装) 数据库驱动:mongo-Java-driver-3.0.0.jar 问题描述:shell环境下用mongo客户端程序能正常连接mongod,但java程序始终报错. 查看centos7的官方文档,知道centos7最小安装默认只安装防火墙的动态配置规则服务(firewalld),静态配置规则服务(iptables与ip6tables)需要另行安装. 1.关闭firewall: systemctl st

  • mongodb replica set 添加删除节点的2种方法

    一,利用rs.reconfig,来添加,删除节点 1,添加节点 repmore:PRIMARY> config = {_id:"repmore",members:[{_id:0,host:'127.0.0.1:27017',priority :2},{_id:1,host:'127.0.0.1:27018',priority:1}]}; //添加节点 repmore:PRIMARY> rs.reconfig(config); //使配置生效 repmore:PRIMARY&

  • 关于mongoose连接mongodb重复访问报错的解决办法

    具体代码如下所示: var express = require('express'); var mongoose = require('mongoose'); var router = express.Router(); var Person = mongoose.model('Person',{ id:Number, name:String }); /*新增*/ router.get('/insert', function(req, res){ var student = new Person

  • perl操作MongoDB报错undefined symbol: HeUTF8解决方法

    因为shell操作mongo比较麻烦,只好尝试使用perl操作mongo,perl需要操作mongodb必须先安装相应的驱动,大部分人使用cpan安装,个人觉得太麻烦,使用cpanm安装perl模块. 复制代码 代码如下: # cpanm MongoDB --> Working on MongoDB Fetching http://www.cpan.org/authors/id/F/FR/FRIEDO/MongoDB-0.702.1.tar.gz ... OK Configuring Mongo

  • NodeJS连接MongoDB数据库时报错的快速解决方法

    今天第一次尝试连接MongoDB数据库,具体步骤也很简单. 首先,通过NodeJS运行环境安装MongoDB包,进入要安装的目录,执行语句 npm install mongodb安装成功后,通过如下语句测试与数据库建立连接几关闭数据库 var mongo = require('mongodb'); var host = "localhost"; var port = mongo.Connection.DEFAULT_PORT; //创建MongoDB数据库所在服务器的Server对象

  • mongodb添加arbiter节点的方法示例

    前言 在创建mongodb的replica set的时候,只是做成了1主2从,没有做成1主1从1仲裁.这我们将一个几点从replica set中删除,再以仲裁节点的身份加入到replica set中: 1.初始状态: shard1ReplSet:PRIMARY> rs.status();rs.status(); { "set" : "shard1ReplSet", "date" : ISODate("2017-02-21T07:4

  • Mongodb增加、移除Arbiter节点实例

    增加Arbiter 增加配置文件mongod1,mongod2,mongod3分别为shard1,shard2,shard3三个set集合的arbiter. 复制代码 代码如下: replSet=shard1 replSet=shard2 replSet=shard3 启动mongod实例. 连接Primary. 复制代码 代码如下: rs.addArb("192.168.1.50:10001″) rs.addArb("192.168.1.50:10002″) rs.addArb(&q

  • MongoDB添加仲裁节点报错:replica set IDs do not match的解决方法

    背景: 由于历史原因,某个MongoDB副本集只有一主一从双节点,无法满足自动故障转移要求,需要配置一个仲裁节点. 原有节点192.168.10.20:27017,192.168.10.21:27017,现在准备在20上配置一个新节点27018当做仲裁 在当前主节点上执行 repset:PRIMARY> cfg={_id:"repset", members:[{_id:0, host:'192.168.10.20:27017', priority:1},{_id:2, host:

  • MongoDB添加secondary节点的2种方法详解

    前言 前段时间维护的一个事业群的其中一条业务线的开发找到运维,提出来了一个MongoDB的优化问题,那段时间MongoDB正在从op管理移交给db进行维护,整个部门都对MongoDB的运维经验缺乏,MongoDB的优化更是一个未知的挑战.当op找到我,核心系统的公共服务平台用来进行短信服务的MongoDB集群想进行一次优化,我当仁不能让的承担了这项我都觉得可能搞不定的任务. 开发找到我提出了两点儿问题,并寻求运维团队解决这个问题,不过最终在我的理性的思考和他感性的思维碰撞下,最终我还是以胜利者的

  • Java Swing实现窗体添加背景图片的2种方法详解

    本文实例讲述了Java Swing实现窗体添加背景图片的2种方法.分享给大家供大家参考,具体如下: 在美化程序时,常常需要在窗体上添加背景图片.通过搜索和测试,发现了2种有效方式.下面分别介绍. 1. 利用JLabel加载图片 利用JLabel自带的setIcon(Icon icon)加载icon,并设置JLabel对象的位置和大小使其完全覆盖窗体.这是一个很取巧的办法,代码非常简单,如下所示. JLabel lbBg = new JLabel(imageIcon); lbBg.setBound

  • 使用Java构造和解析Json数据的两种方法(详解一)

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON数据不须要任何特殊的 API 或工具包. 在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面首先介绍用json-lib构造和解析Json数据的方法

  • JS合并两个数组的3种方法详解

    这篇文章主要介绍了JS合并两个数组的3种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 需要将两个数组合并成为一个的情况.比如: var a = [1,2,3]; var b = [4,5,6]; 有两个数组a.b,需求是将两个数组合并成一个.方法如下: 1.concat js的Array对象提供了一个叫concat()方法,连接两个或更多的数组,并返回结果. var c = a.concat(b); //c=[1,2,3,4,5,6]

  • SpringBoot注入配置文件的3种方法详解

    这篇文章主要介绍了SpringBoot注入配置文件的3种方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 方案1:@ConfigurationProperties+@Component 定义spring的一个实体bean装载配置文件信息,其它要使用配置信息是注入该实体bean /** * 将配置文件中配置的每一个属性的值,映射到这个组件中 * @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配

  • Java 不使用第三方变量交换两个变量值的四种方法详解

    目录 变量本身交换数值 算术运算 指针地址操作 位运算 简单总结 哈喽,大家好,我是阿Q.前几天有个小伙伴去面试,被面试官的一个问题劝退了:请说出几种不使用第三方变量交换两个变量值的方法. 问题有点绕,好不容易缕清了面试官的问题,却发现答不上来.一时间尴尬无比,只能硬着头皮说不会. 遇到交换变量值的问题,通常我们的做法是:定义一个新的变量,借助它完成交换. 代码如下: t = a; a = b; b = t; 但问题的重点是"不使用第三方变量",那就变得"可爱"起来

  • Redis实现分布式锁的五种方法详解

    目录 1. 单机数据一致性 2. 分布式数据一致性 3. Redis实现分布式锁 3.1 方式一 3.2 方式二(改进方式一) 3.3 方式三(改进方式二) 3.4 方式四(改进方式三) 3.5 方式五(改进方式四) 3.6 小结 在单体应用中,如果我们对共享数据不进行加锁操作,会出现数据一致性问题,我们的解决办法通常是加锁. 在分布式架构中,我们同样会遇到数据共享操作问题,本文章使用Redis来解决分布式架构中的数据一致性问题. 1. 单机数据一致性 单机数据一致性架构如下图所示:多个可客户访

  • SpringBoot处理接口幂等性的两种方法详解

    目录 1. 接口幂等性实现方案梳理 1.1 基于 Token 1.2 基于请求参数校验 2. 基于请求参数的校验 在上周发布的 TienChin 项目视频中,我和大家一共梳理了六种幂等性解决方案,接口幂等性处理算是一个非常常见的需求了,我们在很多项目中其实都会遇到.今天我们来看看两种比较简单的实现思路. 1. 接口幂等性实现方案梳理 其实接口幂等性的实现方案还是蛮多的,我这里和小伙伴们分享两种比较常见的方案. 1.1 基于 Token 基于 Token 这种方案的实现思路很简单,整个流程分两步:

  • Python实现解析参数的三种方法详解

    目录 先决条件 使用 argparse 使用 JSON 文件 使用 YAML 文件 最后的想法 今天我们分享的主要目的就是通过在 Python 中使用命令行和配置文件来提高代码的效率 Let's go! 我们以机器学习当中的调参过程来进行实践,有三种方式可供选择.第一个选项是使用 argparse,它是一个流行的 Python 模块,专门用于命令行解析:另一种方法是读取 JSON 文件,我们可以在其中放置所有超参数:第三种也是鲜为人知的方法是使用 YAML 文件!好奇吗,让我们开始吧! 先决条件

  • Springboot配置返回日期格式化五种方法详解

    目录 格式化全局时间字段 1.前端时间格式化(不做无情人) 2.SimpleDateFormat格式化(不推荐) 3.DateTimeFormatter格式化(不推荐) 4.全局时间格式化(推荐) 实现原理分析 5.部分时间格式化(推荐) 总结 应急就这样 格式化全局时间字段 在yml中添加如下配置: spring.jackson.date-format=yyyy-MM-dd HH:mm:ss 或者 spring: jackson: ## 格式为yyyy-MM-dd HH:mm:ss date-

随机推荐