MongoDB的复制一:复制的原理
1.复制的角色
复制有三种角色:primay:主库,执行所有的写操作,并把日志写入oplog里。
secondary:复制主库的所有操作。读取主库的oplog,并执行日志里的内容。默认情况下,客户端不能读取secondary的数据。
arbiter:仲裁者,在主库失败后选举主库。该角色是可选的角色。仲裁者不包含数据,客户端不可连接。
2.主库
primay:主库,执行所有的读写操作,并把日志写入oplog里。
3.备库
secondary:复制主库的所有操作。读取主库的oplog,并执行日志里的内容。在主库宕机后,备库可以切换为主库
备库可做以下配置:
1)设置成priority 0的备库,使其不能成为主库。其它特性不变。
2)设置成应该程序不能访问
3)做主库的延迟快照。即延迟复制。延迟的备库必须是priority 0的,并且应该设置为hidden.
3.仲裁者
不包含数据,只负责极少工作,对机器的要求低,可以放在性能差一点的机器上。
每个arbiter可以投一票。其实,每个主库备库都可以投一票
注意:不能在主库或者备库上建仲裁者.(不是不能建,但是会影响投票的准确性)。只在节点数为偶然的集群中加入仲裁者。
4.关于容灾
节点数与可以宕机的节点的个数(否则会产生选举不出primary的问题):
(foolr(N/2)+1) 至少一半成员状态正常,否则不能选举出primary(待验证)
关于选举:
http://www.itpub.net/thread-1740982-1-1.html三台机器的两种复制形式:
3.其它细节
心跳:节点之间每2秒检查心跳,如果一个节点10秒内没有响应就认为该节点失效。
优先级:各节点都有优先级,优先级越高的备库越有希望成为转换为主库
无选举权节点:一个复制群最多可以有50个节点,但是只有7个有选举权,剩下的没有选举权但是可以复制数据
4.回滚
“回滚”:如果备库没有跟上主库的节奏,而主库又挂了,选举了新的主库,之前的主库又加入到复制群中做为备库,
此时之前的主库会做回滚操作。
MongoDB会把回滚数据写入到rollback/目录下,dba来决定是忽视还是使用这些数据。
避免回滚:
默认情况下write concern {w: 1} 确保主库写完后就返回给client,可以改成w: majority ,大多数据写完后返回结果给客户端。但显示可能增加响应时间。
回滚的限制:
默认下,只能回滚300M以内的数据,如果大于300M,日志中有以一警告:
[replica set sync] replSet syncThread: 13410 replSet too much data to roll back
此时必须“save the data directly”(语义不明),或者执行初始化。执行初始化同步,必须删除--dbpaht目录下的内容。。。
详情略。
5.设置默认的write concern
db.products.insert(
{ writeConcern: { w: 2, wtimeout: 5000 } } #至少写入一个从结点
)
{ w: "majority", wtimeout: 5000 }
6.设置复制的可读性
默认下,应用只能读主库。
详情略。
7.oplog的大小
默认以5%的磁盘空间做oplog。详情略。
8.数据同步
有两种方式数据同步:initial sync和Replication
initical sync:初始化数据。
当集群新增加了成员,新成员没有数据,从开始复制数据。
过程如下 :
1)从数据源复制所有数据库,只复制有效的数据。这个步骤中,_id上的索引也被建立。从3.0开始如果发现无效数据,将会在日志中记录:Cloner: found corrupt document in <collection>.
2)使用oplog同步数据。
3)在各集合上建立索引
replication:复制,即正常的数据同步。
支持多线程复制数据。
9.master-slave 复制
mongodb的复制分为两种。旧的称为master-slave复制,没有自动failover功能。新的称为replication sets,有自动failover功能。
这时介绍master-slave复制。
创建master:
mongod --master --dbpath /data/masterdb/
创建salve:
mongod --slave --source <masterhostname><:<port>> --dbpath /data/slavedb/
这样就创建了一个主备。
配置mster-slave:
slave上,master的信息保存在此处:
use local
db.sources.find()#包含了source信息.
db.sources.insert( { host: <masterhostname> <,only: databasename> } ); slave 无法同步数据
slave无法同步数据的原因:备库的复制进度落后太远了。
一旦备库的复制进度落后主库太完,复制就会中断。
解决办法:
必须执行resync命令重新同步数据(应该是initical sync,即重0数据开始同步)。如果在启动slave时加上--autoresync,则slave在复制中断10秒后自动执行resync。
为了避免这种情况,在master启动的时候,可以设置--oplogSize参数,设置较大的日志文件。默认使用磁盘的5%做为oplog,32位上最小是50M,64位上最小1G.
运行时检查配置情况:
2.6以前,在master执行 db.printReplicationInfo() local库。2.6以后,执行rs.printReplicationInfo().
2.6以前,在slave执行 db.printSlaveReplicationInfo() local库。2.6以后,执行rs.printSlaveReplicationInfo().
安全性:
如果master启用了authorization,需要配置keyfile,slave通过keyfile与master通信。
在config文件里加入:
keyFile=/usr/local/mongo/ keyfile
keyFile的内容在各个节点上必须一样。可以使用OpenSSL来生成一个keyfile.
master-slave的failover:
master-slave的角色切换:
从master的磁盘快照创建slave:
从slave的磁盘快照创建slave:
resync复制进度过慢(无法继续复制)的salve:
use admin
db.runCommand( { resync: 1 } )
修改slave的配置信息然后重启slave:
use local
db.sources.update( { host : "prod.mississippi" },
{ $set : { host : "prod.mississippi.example.net" } } )
10. replication sets
配置replication sets
1)以--replSet启动mongod
mongod --replSet "rs0" -f /usr/local/mongodb-linux-x86_64-3.2.0/mongodb.conf
2)进入mong shell
>mongo
3)初始化复制集(没有配置信息)
> rs.initiate();
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "node1:27017",
"ok" : 0,
"errmsg" : "No host described in new configuration 1 for replica set repl0 maps to this node",
"code" : 93
}
4)验证初始配置
> rs.conf();
2015-12-27T11:04:15.763+0800 E QUERY [thread1] Error: Could not retrieve replica set config: {
"info" : "run rs.initiate(...) if not yet done for the set",
"ok" : 0,
"errmsg" : "no replset config has been received",
"code" : 94
} :
rs.conf@src/mongo/shell/utils.js:1090:11
@(shell):1:1
> cfg={"_id":"rs0","version":1,"members":[{"_id":1,"host":"192.168.75.10:27017"}]};
{
"_id" : "rs0",
"version" : 1,
"members" : [
{
"_id" : 1,
"host" : "192.168.75.10:27017"
}
]
}
> rs.initiate(cfg);
{ "ok" : 1 }
5)在复制集中加入节点
rs0:PRIMARY> rs.add("192.168.75.11:27017");
{ "ok" : 1 }
rs0:PRIMARY> rs.add("192.168.75.12:27017");
{ "ok" : 1 }
6)检查复制集状态
rs0:PRIMARY> rs.status();
{
"set" : "rs0",
"date" : ISODate("2015-12-27T03:13:26.970Z"),
"myState" : 1,
"term" : NumberLong(1),
"heartbeatIntervalMillis" : NumberLong(2000),
"members" : [
{
"_id" : 1,
"name" : "192.168.75.10:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 575,
"optime" : {
"ts" : Timestamp(1451185968, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2015-12-27T03:12:48Z"),
"electionTime" : Timestamp(1451185613, 2),
"electionDate" : ISODate("2015-12-27T03:06:53Z"),
"configVersion" : 3,
"self" : true
},
{
"_id" : 2,
"name" : "192.168.75.11:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 41,
"optime" : {
"ts" : Timestamp(1451185968, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2015-12-27T03:12:48Z"),
"lastHeartbeat" : ISODate("2015-12-27T03:13:24.973Z"),
"lastHeartbeatRecv" : ISODate("2015-12-27T03:13:26.011Z"),
"pingMs" : NumberLong(1),
"syncingTo" : "192.168.75.10:27017",
"configVersion" : 3
},
{
"_id" : 3,
"name" : "192.168.75.12:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 37,
"optime" : {
"ts" : Timestamp(1451185968, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2015-12-27T03:12:48Z"),
"lastHeartbeat" : ISODate("2015-12-27T03:13:25.028Z"),
"lastHeartbeatRecv" : ISODate("2015-12-27T03:13:25.128Z"),
"pingMs" : NumberLong(1),
"configVersion" : 3
}
],
"ok" : 1
}
7) 测试复制情况:
rs0:PRIMARY> use testdb
switched to db testdb
rs0:PRIMARY> db.test1231.insert({"name":"test repl"});
WriteResult({ "nInserted" : 1 })
rs0:SECONDARY> rs.slaveOk();
rs0:SECONDARY> use testdb
switched to db testdb
rs0:SECONDARY> show collections;
test1231
rs0:SECONDARY> db.test1231.find();
{ "_id" : ObjectId("567f58f221cb21ff2f187d33"), "name" : "test repl" }
节点重新同步resync:
如果一个节点没有数据,将该结点加入到replication set中,则会自动的完全同步数据。
把一个有数据的结点加入到replication set中:清除--dbpath目录中的内容重启该实例即可。
8)replication set的一些设置
参照:
设置优先级,是否隐藏及延时
cfg = rs.conf()
cfg.members[0].priority = 0
cfg.members[0].hidden = true
cfg.members[0].slaveDelay = 3600
rs.reconfig(cfg)
设置选举权:
cfg = rs.conf()
cfg.members[3].votes = 0
cfg.members[4].votes = 0
cfg.members[5].votes = 0
rs.reconfig(cfg)