热门标签 | 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核心概念详解
    本文介绍了NoSQL数据库的概念及其应用场景,重点解析了MongoDB的基本特性、数据结构以及常用操作。MongoDB是一个高性能、高可用且易于扩展的文档数据库系统。 ... [详细]
  • 本文介绍了如何使用Node.js通过两种不同的方法连接MongoDB数据库,包括使用MongoClient对象和连接字符串的方法。每种方法都有其特点和适用场景,适合不同需求的开发者。 ... [详细]
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • 本文详细介绍了如何搭建一个高可用的MongoDB集群,包括环境准备、用户配置、目录创建、MongoDB安装、配置文件设置、集群组件部署等步骤。特别关注分片、读写分离及负载均衡的实现。 ... [详细]
  • 深入解析:存储技术的演变与发展
    本文探讨了从单机文件系统到分布式文件系统的存储技术发展过程,详细解释了各种存储模型及其特点。 ... [详细]
  • Redis:缓存与内存数据库详解
    本文介绍了数据库的基本分类,重点探讨了关系型与非关系型数据库的区别,并详细解析了Redis作为非关系型数据库的特点、工作模式、优点及持久化机制。 ... [详细]
  • Python学习day3网络基础之网络协议篇
    一、互联网协议连接两台计算机之间的Internet实际上就是一系列统一的标准,这些标准称之为互联网协议,互联网的本质就是一系列网络协议。二、为什么要有互联网协议互联网协议就相当于计 ... [详细]
  • Asynchronous JavaScript and XML (AJAX) 的流行很大程度上得益于 Google 在其产品如 Google Suggest 和 Google Maps 中的应用。本文将深入探讨 AJAX 在 .NET 环境下的工作原理及其实现方法。 ... [详细]
  • 华为与红帽联手,加速开源电信软件革新
    华为与红帽携手合作,旨在加速开源电信软件的发展,以满足大型电信运营商对灵活网络解决方案的需求。 ... [详细]
  • 精选10款Python框架助力并行与分布式机器学习
    随着神经网络模型的不断深化和复杂化,训练这些模型变得愈发具有挑战性,不仅需要处理大量的权重,还必须克服内存限制等问题。本文将介绍10款优秀的Python框架,帮助开发者高效地实现分布式和并行化的深度学习模型训练。 ... [详细]
  • 本文介绍如何使用JavaScript中的for循环来创建一个九九乘法表,适合初学者学习循环结构的应用。 ... [详细]
  • 本文介绍了如何利用X_CORBA实现远程对象调用,并通过多个示例程序展示了其功能与应用,包括基础的Hello World示例、文件传输工具以及一个完整的聊天系统。 ... [详细]
  • 本文探讨了一种统一的语义数据模型,旨在支持物联网、建筑及企业环境下的数据转换。该模型强调简洁性和可扩展性,以促进不同行业间的插件化和互操作性。对于智能硬件开发者而言,这一模型提供了重要的参考价值。 ... [详细]
  • 本文详细记录了 MIT 6.824 课程中 MapReduce 实验的开发过程,包括环境搭建、实验步骤和具体实现方法。 ... [详细]
  • 对象存储与块存储、文件存储等对比
    看到一篇文档,讲对象存储,好奇,搜索文章,摘抄,学习记录!背景:传统存储在面对海量非结构化数据时,在存储、分享与容灾上面临很大的挑战,主要表现在以下几个方面:传统存储并非为非结 ... [详细]
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社区 版权所有