mongodb利用rs,实现ha和备份

标签: mongodb 利用 rs | 发表时间:2013-10-01 23:14 | 作者:kyfxbl
出处:http://blog.csdn.net

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()
应该能看到类似下图的效果


二、验证HA场景

用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进行手工恢复

作者:kyfxbl 发表于2013-10-1 15:14:25 原文链接
阅读:30 评论:0 查看评论

相关 [mongodb 利用 rs] 推荐:

mongodb利用rs,实现ha和备份

- - CSDN博客推荐文章
mongodb最简单的部署方式,是单节点部署. 国庆前找了台8核,48G RAM的server,在100+并发的压力下跑了一个星期,也没有发现数据丢失和server负载过大的情况. 但是单节点部署还是有一些问题,第一是无法做HA,如果该mongod down掉,或者部署的server down掉,应用就无法工作;第二是不利于备份,因为在备份的时候,会给mongod额外的负担,有可能影响业务;第三是无法做读写分离.

[mongodb] java操作mongodb

- - 数据库 - ITeye博客
           //实例化Mongo对象,连接27017端口.                               //连接名为yourdb的数据库,假如数据库不存在的话,mongodb会自动建立. //从Mongodb中获得名为yourColleection的数据集合,如果该数据集合不存在,Mongodb会为其新建立.

JAX-RS 从傻逼到牛叉 5:资源的动态定位

- - BlogJava-首页技术区
目前我们的电影服务只提供了对电影信息的访问服务,现在我们要再增加两项级服务,分别用来访问导演和演员信息. 加上原先的电信信息服务,我们把 URI 统一放到. /ms/rest/service/ 的子路径下. 最先想到的方法就是为这三个 URI 分别写 JAX-RS 服务:. // 此处省略若干行 } @Singleton @Path("service/director") public class DirectorService {.

【MongoDB】MongoDB之优化器Profiler

- - CSDN博客数据库推荐文章
在mysql数据库中,慢查询日志经常作为优化数据库的依据, mongodb中依然有类似的功能. Mongodb自带的profiler,可以方便地记录所有耗时的操作,以便于调优;. 一、开始profiler功能. 开启profier功能有两种:. 第一种就是直接在启动参数里面进行设置,就在茄冬mongodb时候添加-profile=级别.

夜说mongodb

- Lianhui Wang - NoSQLFan
前两天本站刚刚分享了wordnik使用MongoDB经验的文章:《Wordnik 的 MongoDB 使用经历》,今天又看到一位朋友对这方面做的总结,分享在这里,供大家参考. 赋闲以后很长没有更新博客了,说忙完全是借口,多半因为没有兴致所致. 今天凌晨比赛多多,趁着比赛的前奏和间隙,遂浏览些技术文章.

MongoDB与内存

- 高春辉 - 火丁笔记
但凡初次接触MongoDB的人,无不惊讶于它对内存的贪得无厌,至于个中缘由,我先讲讲Linux是如何管理内存的,再说说MongoDB是如何使用内存的,答案自然就清楚了. 据说带着问题学习更有效,那就先看一个MongoDB服务器的top命令结果:. 这台MongoDB服务器有没有性能问题. 先讲讲Linux是如何管理内存的.

白话MongoDB(一)

- Ease - 江边潮未尽,枫红一季秋
按照官方的说法,MongoDB是一种可扩展的高性能的开源的面向文档(document-oriented )的数据库,采用C++开发. 注意mongo不是mango(芒果),这个词是从humongous中截取出来的,其野心不言而明,直指海量数据存储. 和其他很多NoSQL不太一样,MongoDB背后有一个专门的商业公司在提供支持和推广,有点类似MySQL AB的模式.

MongoDB 索引

- - 博客园_首页
索引是用来加快查询的,数据库索引与数据的索引类似,有了索引就不需要翻遍整本书,数据库可以直接在索引中查找,. 使得查询速度很快,在索引中找到条目后,就可以直接跳转到目标文档的位置.. 要掌握如何为查询配置最佳索引会有些难度.. MongoDB索引几乎和关系型数据库的索引一样.绝大数优化关系型数据库索引的技巧同样适用于MongoDB..

MongoDB sql操作

- - 数据库 - ITeye博客
1.  基本查询:. 下面的示例等同于SQL语句的where name = "stephen" and age = 35.      --返回指定的文档键值对. 下面的示例将只是返回name和age键值对.      --指定不返回的文档键值对. 下面的示例将返回除name之外的所有键值对.