复制
MongoDB的复制功能很重要,尤其是现在的存储引擎还不支持单击持久性。不仅可以用复制来应对故障切换,数据集成,还可以做读扩展,热备份或作为离线批处理的数据源。
1.主从复制
主从复制是MongoDB最常用的复制方式。可用于备份,故障恢复和读扩展等。
基本就是搭建一个主节点和一个或多个从节点,每个从节点需要知道主节点的地址。运行mongod --master启动主服务器。运行mongod --slave --source master_address启动从服务器。
[root@test02 ~]# mongod --fork --dbpath /data/node2 --logpath /data/mongodb.log --port 10001 --logappend --master
从节点选择不同的目录和端口,并且用--source为从节点指明主节点的地址。
[root@test02 ~]# mongod --fork --dbpath /data/node3 --logpath /data/mongodb2.log --port 10002 --logappend --slave --source localhost:10001
所有的从节点都是从主节点复制信息,目前还不能从节点到从节点复制机制,原因是从节点没有自己的oplog。
一个集群中从节点没有明确的限制,但是多个节点对单点主机发起的查询也是吃不消的,不超过12个节点的集群可以良好运转。
1.1 选项
(1)--only
在从节点上指定只复制特定某个数据库(默认复制所有数据库)。
(2)--slavedelay
用在从节点上,当应用主节点的操作时增加延迟。这样可以轻松设置延时从节点了,这样的节点对于用户无意间删除重要数据或插入垃圾数据起到防护作用。通过延缓操作,可以有个恢复时间差。
(3)--fastsync
以主节点的数据快照为基础启动从节点。如果数据目录一开始时主节点的数据快照,从节点用这个选项启动要比做完整同步块很多。
(4)--autoresync
如果主节点和从节点不同步,可以自动同步了。
(5)--oplogsuze
主节点oplog的大小(单位是MB)。
1.2 添加以及删除源
cat >> /etc/hosts <<EOF 192.168.27.212 test02 192.168.27.213 test03 192.168.27.214 test01 EOF</div>
启动从节点时可以用--source指定主节点,也可以在shell中配置这个源。
[root@test02 ~]# mongod --fork --dbpath /data/node3 --logpath /data/mongodb.log --port 10003 --logappend --slave
将192.168.27.212:10001作为源插入到从节点上。
> db.sources.insert({ "host" : "192.168.27.212:10001"});</div>
立即查询会得到插入的文档:
> use local switched to db local > db.sources.find(); { "_id" : ObjectId("530be5049ab1ad709cfe66b7"), "host" : "test02:10001"</div>
当同步完成后,文档更新:
> db.sources.find(); { "_id" : ObjectId("530bf0ab058022d91574c79c"), "host" : "test02:10001", "source" : "main", "syncedTo" : Timestamp(1393291443, 1), "dbsNextPass" : { "foo" : true, "test" : true } }</div>
2.副本集
副本集就是有自动故障恢复功能的主从集群。主从集群和副本集最为明显的区别就是副本集没有固定的主节点:整个集群会选举出一个主节点,当其不能工作时,则变更到其它节点。副本集总会有一个活跃节点和一个或多个备份节点。
副本集最好的优点就是全自动化的。
mongod --fork --dbpath /data/node2 --logpath /data/mongodb.log --port 10001 --logappend --replSet myrepl/test03:10002 mongod --fork --dbpath /data/node3 --logpath /data/mongodb.log --port 10002 --logappend --replSet myrepl/test02:10001</div>
副本集的亮点是自检测功能:在其中指定单台服务器后,MongoDB会自动搜索并连接其余的节点。
启动几台服务器后,日志会告诉你副本集没有初始化。需要在shell中初始化副本集。
连接任意一个服务器。初始化命令只执行一次:
> db.runCommand({"replSetInitiate" : { ... "_id" : "myrepl", ... "members" : [ ... { ... "_id" : 1, ... "host" : "test02:10001" ... }, ... { ... "_id" : 2, ... "host" : "test03:10002" ... } ... ]}}) { "startupStatus" : 4, "info" : "myrepl/test03:10002", "ok" : 0, "errmsg" : "all members and seeds must be reachable to initiate set" }</div>
(1)"_id" : "myrepl" 副本集的名称
(2)"members" : [...] 副本集中的服务器列表,每个服务器至少两个键。
(3)"_id" : N 每个服务器唯一的ID
(4)"host" : hostname 这个键指定服务器主机
或者:
config = {"_id" : "myrepl", "members" : [ {"_id" : 0, "host" : "test02:10001"}, {"_id" : 1, "host" : "test03:10002"} ]} rs.initiate(config); rs.status(); myrepl:SECONDARY> rs.status(); { "set" : "myrepl", "date" : ISODate("2014-02-25T02:17:39Z"), "myState" : 2, "syncingTo" : "test03:10002", "members" : [ { "_id" : 0, "name" : "test02:10001", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 968, "optime" : Timestamp(1393294457, 1), "optimeDate" : ISODate("2014-02-25T02:14:17Z"), "errmsg" : "syncing to: test03:10002", "self" : true }, { "_id" : 1, "name" : "test03:10002", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 48, "optime" : Timestamp(1393294457, 1), "optimeDate" : ISODate("2014-02-25T02:14:17Z"), "lastHeartbeat" : ISODate("2014-02-25T02:17:38Z"), "lastHeartbeatRecv" : ISODate("2014-02-25T02:17:39Z"), "pingMs" : 1, "syncingTo" : "test02:10001" } ], "ok" : 1 }</div>
如果这时候把primary节点停掉,在secondary节点执行写操作,就会发生如下错误提示:
myrepl:SECONDARY> db.test.insert({name : "baobao"});</div>
not master</div>
如果只有2台Mongodb,配置复制集群还不够安全,需要1个外在角色调整各个节点的角色。
(1)standard:常规节点,存储一份完整的数据副本,参与选举投票,可能称为活跃节点。
(2)passive:存储完整的数据副本,参与投票,不能成为活跃节点。
(3)arbiter:仲裁者只负责投票,不接受复制数据,也不能成为活跃节点。
当Primary宕掉后,可以通过Arbiter在Secodarys中选举一个Primary节点,避免单点故障。
可以增加一个仲裁节点,只负责仲裁,不做数据存储。
mongod --fork --dbpath /data/node1 --logpath /data/mongodb.log --port 10003 --logappend --replSet myrepl/test02:10001,test03:10002 myrepl:PRIMARY> rs.addArb("test01:10003"); { "ok" : 1 }</div>
查看各节点的状态:
myrepl:PRIMARY> rs.status(); { "set" : "myrepl", "date" : ISODate("2014-02-25T02:30:26Z"), "myState" : 1, "members" : [ { "_id" : 0, "name" : "test02:10001", "health" : 1, "state" : 1, "stateStr" : "PRIMARY", "uptime" : 1735, "optime" : Timestamp(1393295409, 1), "optimeDate" : ISODate("2014-02-25T02:30:09Z"), "self" : true }, { "_id" : 1, "name" : "test03:10002", "health" : 1, "state" : 2, "stateStr" : "SECONDARY", "uptime" : 204, "optime" : Timestamp(1393295409, 1), "optimeDate" : ISODate("2014-02-25T02:30:09Z"), "lastHeartbeat" : ISODate("2014-02-25T02:30:26Z"), "lastHeartbeatRecv" : ISODate("2014-02-25T02:30:24Z"), "pingMs" : 1, "syncingTo" : "test02:10001" }, { "_id" : 2, "name" : "test01:10003", "health" : 1, "state" : 6, "stateStr" : "UNKNOWN", "uptime" : 17, "lastHeartbeat" : ISODate("2014-02-25T02:30:25Z"), "lastHeartbeatRecv" : ISODate("1970