参考:http://wengzhijuan12.blog.163.com/blog/static/3622414520137104257376/

http://wengzhijuan12.blog.163.com/blog/static/3622414520137104257376/

http://blog.csdn.net/shmnh/article/details/41976451

复制升级版的主从复制,它实现了故障自动转移功能,同时从节点支持读

一,节点类型:

a)    主节点:支持读写

b)    从节点:支持读(需设置)

c)    仲裁节点:参与投票同时也支持读(需设置)

二,实验

主节点:192.168.129.47

从节点:192.168.129.48

仲裁节点:192.168.129.49

 

1.主节点配置如下:

vi  /etc/rc.local

rm /usr/mongodb/log/mongodb.log

/usr/mongodb/bin/mongod --dbpath=/usr/mongodb/data/ --logpath=/usr/mongodb/log/mongodb.log --port 27017  --replSet test --keyFile /data/key/tbtest/ --maxConns=2000 --fork –logappend

 

从节点配置如下:

vi /etc/rc.local

rm /usr/mongodb/log/mongodb.log

/usr/mongodb/bin/mongod --dbpath=/usr/mongodb/data/ --logpath=/usr/mongodb/log/mongodb.log --port 27017  --replSet test --keyFile /data/key/tbtest/ --maxConns=2000 --fork –logappend

 

 

这里需要注意加上 ----keyFile 参数.  加上后 mongodb 默认开启 auth .  

echo "tb test 1" > /data/key/tb

chmod 600 /data/key/tb

配置完成之后启动mongod服务

2.启动之后在主节点执行(192.168.129.47)

use admin

db.runCommand({"replSetInitiate" : {

"_id" : "test",

"members" : [

{

"_id" : 0,

"host" : "192.168.129.47:27017"

},

{

"_id" : 1,

"host" : "192.168.129.48:27017"

} ,

{

"_id" : 2,

"host" : "192.168.129.49:27017"

}

]}})

 

#查看复制集状态
rs.status()
rs.isMaster()
rs.conf()

 

#查看从库状态
db.printSlaveReplicationInfo()

 

#设置从库可查询
db.getMongo().setSlaveOk()
rs.setSlaveOk()

 

 

#增加复制集节点
1.锁定已有的一个从库,并将缓存中的数据写入到磁盘
use admin
db.runCommand({"fsync":1, "lock":1})
2.拷贝被锁定从库的数据文件到新建从库的数据目录
3.解锁从库
db.$cmd.sys.unlock.findOne()
db.currentOp()
4.启动新从库
./mongod --replSet rs1 --keyFile /data/set/key/r4 --fork --port 28014 --dbpath /data/set/r4 --logpath=/data/set/log/r4.log --logappend --fastsync
5.rs.add("localhost:27017")

6.删除节点:

  主节点上面执行 rs.remove(“ip:port”)

 
对于replica set 中的secondary 节点默认是不可读的。在写多读少的应用中,使用Replica Sets来实现读写分离。通过在连接时指定或者在主库指定slaveOk,由Secondary来分担读的压力,Primary只承担写操作。

如果通过shell访问mongo,要在secondary进行查询。会出现如下错误:

imageSet:SECONDARY> db.fs.files.find()
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }
有两种方法实现从机的查询:
第一种方法:db.getMongo().setSlaveOk();
第二种方法:rs.slaveOk();
但是这种方式有一个缺点就是,下次再通过mongo进入实例的时候,查询仍然会报错,为此可以通过下列方式

vi ~/.mongorc.js

增加一行rs.slaveOk();
这样的话以后每次通过mongo命令进入都可以查询了
 
如果是通过java访问secondary的话则会报下面的异常
com.mongodb.MongoException: not talking to master and retries used up
解决的办法很多。
第一种方法:在java代码中调用dbFactory.getDb().slaveOk();
第二种方法:在java代码中调用
dbFactory.getDb().setReadPreference(ReadPreference.secondaryPreferred());//在复制集中优先读secondary,如果secondary访问不了的时候就从master中读

dbFactory.getDb().setReadPreference(ReadPreference.secondary());//只从secondary中读,如果secondary访问不了的时候就不能进行查询
第三种方法:在配置mongo的时候增加slave-ok="true"也支持直接从secondary中读
<mongo:mongo id="mongo" host="${mongodb.host}" port="${mongodb.port}">
        <mongo:options slave-ok="true"/> 
</mongo:mongo>
 

随着web2.0兴起,高并发大数据量的应用对数据库快速响应的性能要求日趋明显,传统的关系型数据库在这方面显得有些乏力。有矛自有盾,内存DB的出现弥补了传统关系型db的不足。目前市面流行的内存db主要有redis、memcach、mongodb。前面二者是基于key-value形式存储,而mongodb是基于关系型数据库表的一些特性的存储方式,并支持索引。所以在一些对大数据量、数据关联度有要求的场景下,mongodb是一种不错选择。

Replica Set是mongodb的一个副本集群方案,它优越于传统的数据库主从方式。传统的主从方式,master负责读写,slaver负责从master同步数据,一旦master宕机,slaver就废了,这种方式在灾备方面有缺陷,而mongodb的Replica Set的集群机制解决了这种缺陷。

Replica Set

主要分为:primary(主节点,提供增删查改服务),slaver(备节点,只提供读),arbiter(仲裁节点,不存储数据,只负责仲裁)。

流程:client从primary节点读写数据,slaver从primary那里同步数据,当primary宕机时候,arbiter会在10秒内从众多slaver节点中选出一个健康的slaver顶替primary,这样就减轻了灾害arbiter节点本身不存储数据,只是监测集群中primary和slaver的运行情况(如果arbiter宕机,整个集群也就废了,唯一的不足之处)。slaver只提供读的功能,不能写,我们的项目查询的需求可以去连slaver节点,这样就大大减轻了primary主节点的负载。

以下是Replica Set的流程图:

 

Replica Set的原理我们明白了,你可能会问,我们在编程的时候,对于primary、slaver这么多db,我们一定是往primary节点写数据,如果primary节点宕机了,程序应该怎么检测,怎么找到新的primary节点呢?

不用担心,mongodb已经解决了你的疑问。mongodb提供了对各类语言的驱动的支持,你只需调用Replica Set接口,然后参照说明来使用它,下面以node.js

 

var Db = require('mongodb').Db,

    Server = require('mongodb').Server,
    ReplSet = require('mongodb').ReplSet;

//集群Server地址
var serverAddr = {
    9001: '192.168.1.100', //节点1
    9002: '192.168.1.100', //节点2
    9003: '192.168.1.100'  //节点3
}

//集群Sever对象的集合
var servers = [];
for (var i in serverAddr) {
    servers.push(new Server(serverAddr[i], parseInt(i)));

}

 

var replStat = new ReplSet(servers, {});

var db = new Db('blog', replStat);

//mongodb操作
db.open(function(err, db) {
    var collection = db.collection('user');
    //查询一个document
    collection.findOne({
        name: 'jerry'
    }, function(err, results) {
        console.info('query:', results);
    });
    //插入一个document
    collection.insert({
        name: 'ok',
        age: 28
    }, function(err, results) {
        console.info('insert:' + results);
    });

});

 

上面配置了几个节点9001、9002、9003,我们无需关注哪个是主节点、备节点、冲裁节点,驱动会自动判断出一个健康的主节点来给node,我们只需专心写数据库的操作逻辑就可以了。

但这里存在一个问题,Replica Set在切换节点的时候,会出现一个断档期,我们知道node是异步/O的,在这个断档期,如果node在执行大量操作的话,弱小的栈内存会溢出,报:RangeError: Maximum call stack size exceeded错误,这个错误是系统级错误,会导致app崩掉,即使捕获异常或等db切换完成,程序依然会挂死在哪里。目前还没找到解决的方法,正在研究mongo驱动的api,试图通过一个体现切换过程状态监听的事件解决,如果该事件触发,则停止db操作,待切换完成后再恢复,这样应该可以解决问题。