热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

MongoDB利用rs实现ha和备份

mongodb最简单的部署方式,是单节点部署。国庆前找了台8核,48GRAM的server,在100+并发的压力下跑了一个星期,也没有发现数据丢失和server负载过大的情况但是单节点部署还是有一些问题,第一是无法做HA,如果该mongoddown掉,或者部署的serverdown掉,应

mongodb最简单的部署方式,是单节点部署。国庆前找了台8核,48G RAM的server,在100+并发的压力下跑了一个星期,也没有发现数据丢失和server负载过大的情况

但是单节点部署还是有一些问题,第一是无法做HA,如果该mongod down掉,或者部署的server down掉,应用就无法工作;第二是不利于备份,因为在备份的时候,会给mongod额外的负担,有可能影响业务;第三是无法做读写分离。所以在生产环境下,依然考虑使用集群部署

mongod支持的集群部署方式有3种:

1、master-slave

2、replica set

3、sharding

master-slave可以解决备份的问题,但是无法透明地HA,所以也不大好;sharding是mongo的一个亮点特性,可以自动分片。但是根据我的测试结果,在单集合达到2000万条数据的门槛之后,sharding才开始体现出性能优势,而sharding的数据是分布式的,所以备份会比较复杂,而且也需要更多的服务器,不利于成本。所以最后我考虑,还是使用replica set方式的集群部署比较合适。可以解决HA,备份,读写分离的问题

基本上采用官网推荐的这种TOPO:

理想情况下,最好是3个mongod实例分别部署在单独的server上,这样就需要3台server;出于成本考虑,也可以把2台secondary部署在同一台server上,primary由于要处理读写请求(读写暂不分离),需要很多内存,并且考虑HA因素,所以primary是需要保证独占一台server比较好,这样就一共需要2台server

1、以-replset方式启动mongod

也可以用命令行启动,但是不利于管理,所以建议采用--config参数来启动,配置文件如下:

port=2222
bind_ip=127.0.0.1
dbpath=/home/zhengshengdong/mongo_dbs/db1
fork=true
replSet=rs0
logpath=/home/zhengshengdong/mongo_log/log1.txt
logappend=true
journal=true

./mongod --config /home/zhengshengdong/mongo_confs/mongo1.conf

然后如法炮制,启动另外2个mongod实例

2、初始化replica set,并添加secondary

用./mongo --port 2222连上准备作为primary的mongod实例,然后依次执行以下命令

rs.initiate()
rs.conf()
rs.add("host1:port")
rs.add("host2:port")
3、检查

在primary实例上执行

rs.status()
应该能看到类似下图的效果

用java driver写了以下代码来做验证

public static void main(String[] args) throws UnknownHostException {
                ScheduledExecutorService executor = Executors
                                .newSingleThreadScheduledExecutor();
                final MongoClient client = MongoConnectionHelper.getClient();
                client.setWriteConcern(WriteConcern.REPLICA_ACKNOWLEDGED);
                Runnable task = new Runnable() {
                        private int i = 0;
                        @Override
                        public void run() {
                                DB db = client.getDB("yilos");
                                DBCollection collection = db.getCollection("test");
                                DBObject o = new BasicDBObject("name", "MongoDB" + i).append(
                                                "count", i);
                                try {
                                        collection.insert(o);
                                } catch (Exception exc) {
                                        exc.printStackTrace();
                                }
                                i++;
                        }
                };
                executor.scheduleWithFixedDelay(task, 1, 1, TimeUnit.SECONDS);
        }
每隔1秒往集群中写入一条数据,然后手动把primary shutdown,观察发现jvm console给出提示:

警告: Primary switching from zhengshengdong-K43SA/127.0.0.1:2222 to mongodb.kyfxbl.net/127.0.0.1:4444

这条消息说明,mongo自动处理了primary的切换,对于应用来说是透明的。然后查看mongo中的记录,发现在切换完成以后,写入操作确实继续了

上图可以看到,在2926和2931之间,正在进行primary倒换,在完成之后,应用可以继续写入数据

但是明显可以看到,中间2927,2928,2929,2930这4条数据丢失了(mongo的primary倒换大约需要3-5秒时间),对于业务来说,虽然时间不长,但是如果因此丢失了业务数据,也是不能接受的

接下来再仔细看下这段时间内代码抛出的异常:

com.mongodb.MongoException$Network: Write operation to server mongodb.kyfxbl.net/127.0.0.1:4444 failed on database yilos
at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:153)
at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:115)
at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:248)
at com.mongodb.DBApiLayer$MyCollection.insert(DBApiLayer.java:204)
at com.mongodb.DBCollection.insert(DBCollection.java:76)
at com.mongodb.DBCollection.insert(DBCollection.java:60)
at com.mongodb.DBCollection.insert(DBCollection.java:105)
at com.yilos.mongo.HATester$1.run(HATester.java:36)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:317)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:150)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$101(ScheduledThreadPoolExecutor.java:98)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.runPeriodic(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:204)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
Caused by: java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
at org.bson.io.PoolOutputBuffer.pipe(PoolOutputBuffer.java:129)
at com.mongodb.OutMessage.pipe(OutMessage.java:236)
at com.mongodb.DBPort.go(DBPort.java:133)
at com.mongodb.DBPort.go(DBPort.java:106)
at com.mongodb.DBPort.findOne(DBPort.java:162)
at com.mongodb.DBPort.runCommand(DBPort.java:170)
at com.mongodb.DBTCPConnector._checkWriteError(DBTCPConnector.java:100)
at com.mongodb.DBTCPConnector.say(DBTCPConnector.java:142)
... 16 more

可以发现,实际上这并不是mongo的问题,而是这段测试代码的BUG引发的:

Runnable task = new Runnable() {
                        private int i = 0;
                        @Override
                        public void run() {
                                DB db = client.getDB("yilos");
                                DBCollection collection = db.getCollection("test");
                                DBObject o = new BasicDBObject("name", "MongoDB" + i).append(
                                                "count", i);
                                try {
                                        collection.insert(o);
                                } catch (Exception exc) {
                                        exc.printStackTrace();
                                }
                                i++;
                        }
                };
问题就出在这里,try语句中捕获到了MongoException,但是我写的这段代码并没有进行处理,只是简单地打印出异常,然后把i自增后,进入下一次循环。

从中也可以判断出,mongo提供的java driver,并不会把失败的write操作暂存在队列中,稍后重试;而是抛弃这次写操作,抛出异常让客户端自行处理。我觉得这个设计也是没问题的,但是客户端一定要进行处理才行。由于后续是使用js driver,所以对这段代码就不继续深究了,在js driver中再仔细研究。关键是要处理mongo exception,以及设置较高级别的write concern。replica set本身的HA机制是可行的

采用2级备份:

第一层是集群部署提供的天然备份,由于存在2个secondary node,会和primary始终保持同步,因此在任何时候,集群都有2份完整的数据镜像副本

第二层则是使用mongo提供的mongodump和fsync工具,通过手工执行或者脚本的方式,在业务负载低(凌晨3点)的时候,获取关键collection的每日副本。备份采集也在secondary上执行,不给primary额外的压力

采集的备份,保存到本地或者其他的server,避免server的存储损坏,造成数据和备份全部丢失

同时,在启动mongod的时候,打开journal参数,这样在极端情况下(上述2种备份都失败),还可以通过oplog进行手工恢复


推荐阅读
  • mongoDB高可用集群环境搭建
    2019独角兽企业重金招聘Python工程师标准在生产环境下,部署一台mongodb服务的话,会存在以下问题:单点问题生产环境是一个 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • Allegro总结:1.防焊层(SolderMask):又称绿油层,PCB非布线层,用于制成丝网印板,将不需要焊接的地方涂上防焊剂.在防焊层上预留的焊盘大小要比实际的焊盘大一些,其差值一般 ... [详细]
  • Abp+MongoDb改造默认的审计日志存储位置
    一、背景在实际项目的开发当中,使用AbpZero自带的审计日志功能写入效率比较低。其次审计日志数据量中后期十分庞大,不适合与业务数据存放在一起。所以我们可以重新实现A ... [详细]
  •     系统采用jeeplus框架(ssm+redis+shiro+mongodb+redis),默认是做了JSP未做前后端分离,由于业务需要已经多终端使用的需求(H5、小程序等) ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • 本文总结了初学者在使用dubbo设计架构过程中遇到的问题,并提供了相应的解决方法。问题包括传输字节流限制、分布式事务、序列化、多点部署、zk端口冲突、服务失败请求3次机制以及启动时检查。通过解决这些问题,初学者能够更好地理解和应用dubbo设计架构。 ... [详细]
  • 云原生应用最佳开发实践之十二原则(12factor)
    目录简介一、基准代码二、依赖三、配置四、后端配置五、构建、发布、运行六、进程七、端口绑定八、并发九、易处理十、开发与线上环境等价十一、日志十二、进程管理当 ... [详细]
  • 像跟踪分布式服务调用那样跟踪Go函数调用链 | Gopher Daily (2020.12.07) ʕ◔ϖ◔ʔ
    每日一谚:“Acacheisjustamemoryleakyouhaven’tmetyet.”—Mr.RogersGo技术专栏“改善Go语⾔编程质量的50个有效实践” ... [详细]
  • 基于分布式锁的防止重复请求解决方案
    一、前言关于重复请求,指的是我们服务端接收到很短的时间内的多个相同内容的重复请求。而这样的重复请求如果是幂等的(每次请求的结果都相同,如查 ... [详细]
  • [翻译]微服务设计模式5. 服务发现服务端服务发现
    服务之间需要互相调用,在单体架构中,服务之间的互相调用直接通过编程语言层面的方法调用就搞定了。在传统的分布式应用的部署中,服务地 ... [详细]
  • ZooKeeper 学习
    前言相信大家对ZooKeeper应该不算陌生。但是你真的了解ZooKeeper是个什么东西吗?如果别人面试官让你给他讲讲ZooKeeper是个什么东西, ... [详细]
author-avatar
硕之缘2010
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有