MongoDB权威指南——复制

MongoDB权威指南

第九章 复制
MongoDB的复制功能很重要,尤为是如今的存储引擎还不支持单击持久性。不只能够用复制来应对故障切换,数据集成,还能够作读扩展,热备份或做为离线批处理的数据源。

9.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个节点的集群能够良好运转。

9.1.1 选项
--only
在从节点上指定只复制特定某个数据库(默认复制全部数据库)。
--slavedelay
用在从节点上,当应用主节点的操做时增长延迟。这样能够轻松设置延时从节点了,这样的节点对于用户无心间删除重要数据或插入垃圾数据起到防御做用。经过延缓操做,能够有个恢复时间差。
--fastsync
以主节点的数据快照为基础启动从节点。若是数据目录一开始时主节点的数据快照,从节点用这个选项启动要比作完整同步块不少。
--autoresync
若是主节点和从节点不一样步,能够自动同步了。
--oplogsuze
主节点oplog的大小(单位是MB)。
9.1.2 添加以及删除源
cat >> /etc/hosts <<EOF
192.168.27.212 test02
192.168.27.213 test03
192.168.27.214 test01
EOF
启动从节点时能够用--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"});
当即查询会获得插入的文档:
> use local
switched to db local
> db.sources.find();
{ "_id" : ObjectId("530be5049ab1ad709cfe66b7"), "host" : "test02:10001" 
当同步完成后,文档更新:
> db.sources.find();
{ "_id" : ObjectId("530bf0ab058022d91574c79c"), "host" : "test02:10001", "source" : "main", "syncedTo" : Timestamp(1393291443, 1), "dbsNextPass" : { "foo" : true, "test" : true } }

9.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
副本集的亮点是自检测功能:在其中指定单台服务器后,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"
}
"_id" : "myrepl"     副本集的名称
"members" : [...]    副本集中的服务器列表,每一个服务器至少两个键。
"_id" : N            每一个服务器惟一的ID
"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
}
若是这时候把primary节点停掉,在secondary节点执行写操做,就会发生以下错误提示:
myrepl:SECONDARY> db.test.insert({name : "baobao"});
not master
若是只有2台Mongodb,配置复制集群还不够安全,须要1个外在角色调整各个节点的角色。
standard:常规节点,存储一份完整的数据副本,参与选举投票,可能称为活跃节点。
passive:存储完整的数据副本,参与投票,不能成为活跃节点。
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 }
查看各节点的状态:
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-01-01T00:00:00Z"),
                 "pingMs" : 1,
                 "lastHeartbeatMessage" : "still initializing"
                }
        ],
        "ok" : 1
}
对比三个节点对自身节点性质的判断:
myrepl:PRIMARY> db.isMaster();
{
        "setName" : "myrepl",
        "ismaster" : true,
        "secondary" : false,
        "hosts" : [
                "test03:10002",
                "test02:10001"
        ],
        "arbiters" : [
                "test01:10003"
        ],
        "primary" : "test03:10002",
        "me" : "test03:10002",
        "maxBsonObjectSize" : 16777216,
        "maxMessageSizeBytes" : 48000000,
        "localTime" : ISODate("2014-02-25T02:32:29.760Z"),
        "ok" : 1
}

myrepl:SECONDARY> db.isMaster();
{
        "setName" : "myrepl",
        "ismaster" : false,
        "secondary" : true,
        "hosts" : [
                "test02:10001",
                "test03:10002"
        ],
        "arbiters" : [
                "test01:10003"
        ],
        "primary" : "test03:10002",
        "me" : "test02:10001",
        "maxBsonObjectSize" : 16777216,
        "maxMessageSizeBytes" : 48000000,
        "localTime" : ISODate("2014-02-25T02:33:50.144Z"),
        "ok" : 1
}

myrepl:SECONDARY> db.isMaster();
{
        "setName" : "myrepl",
        "ismaster" : false,
        "secondary" : true,
        "hosts" : [
                "test02:10001",
                "test03:10002"
        ],
        "arbiters" : [
                "test01:10003"
        ],
        "primary" : "test03:10002",
        "me" : "test02:10001",
        "maxBsonObjectSize" : 16777216,
        "maxMessageSizeBytes" : 48000000,
        "localTime" : ISODate("2014-02-25T02:33:50.144Z"),
        "ok" : 1
}
在节点配置中修改priority键,来配置成标准节点或者被动节点。
默认优先级为1,能够是0~1000.
"arbiterOnly"键能够指定仲裁节点。
备份节点会从活跃节点抽取oplog,并执行操做,就像活跃备份系统中的备份服务器同样。活跃节点也会写操做到本身的本地oplog,这样就能成为活跃节点了。oplog中的操做也包括严格递增的序号。经过序号判断数据的时效性。
9.2.3 故障切换和活跃节点的选举
若是活跃节点坏了,其余节点会选一个新的活跃节点。新的活跃节点由副本集中的大多数选举出来。仲裁节点只负责投票,避免出现僵局。新的节点是优先级最高的节点。
活跃节点使用心跳来跟踪集群中多少节点对其可见,若是不超过半数,则活跃节点自动降为备份节点。能够防止活跃节点一直不放权。
不管活跃节点什么时候变化,新活跃节点的数据被假定为系统的最新数据。其余节点的操做都会回滚,全部节点链接新的活跃节点后要从新同步。这些节点会查看本身的oplog,找出其中活跃节点没有执行过的操做,而后向活跃节点请求这些操做影响的文档的最新副本。
正在执行从新同步的节点被视为恢复中,在完成这个过程前,不能成为活跃节点候选者。

9.3 在从服务器上执行操做
从节点的主要做用是做为故障恢复机制,以防止主节点数据丢失或者中止服务。
能够在从节点作备份的数据源。也能够用来扩展读取性能,或者进行数据处理。
9.3.1 读扩展
用MongoDB扩展读取的一种方式就是将查询放在从节点上,减轻主节点的负载。当负载是读密集型时这样很是不错。当是写密集型时,须要用自动分片来扩展。
使用从节点来扩展MongoDB的读取有个要点,就是数据复制并不一样步,就是在主节点插入或更新数据口,有片刻从节点的数据不是最新的。
扩展读取须要打开一个特殊选项slaveOkey,告诉从服务器是否能够处理请求。
若是直接在secondary上操做,会发生以下错误:
myrepl:SECONDARY> db.test.find();
error: { "$err" : "not master and slaveOk=false", "code" : 13435 }
须要告知Mongodb集群,从哪台机器上进行读操做:
myrepl:SECONDARY> rs.slaveOk();
myrepl:SECONDARY> db.test.find();
{ "_id" : ObjectId("530bfc79eee2c2ce39f9cd95"), "name" : "caoqing" }
{ "_id" : ObjectId("530bfd8f3627cb16c15dcb32"), "name" : "xiaobao" }
9.3.2 用从节点作数据处理
从节点的另一个服务就是做为一种机制来减轻密集型处理的负载,或做为聚合,避免影响主节点的性能。用--master启动一个普通的从节点,同时使用--master和--slave矛盾。这意味着若是能对从节点进行写入,像日常同样查询,就把它做为一个主节点。从节点仍是会不断的从主节点复制数据。这样就能够对从节点执行阻塞操做而不影响主节点的性能。
从节点第一次启动时不能有正在复制的数据库,若是有,数据库就不能完成同步,只能更新。
用这种技术要保证不能对正在复制主节点数据的从节点上的数据库执行写入。从节点不能恢复这些操做,就不能正确的映射主节点。

9.4 工做原理
MongoDB的复制至少须要两台服务器或者节点,其中一个主节点,负责处理客户端请求,其余的都是从节点,负责映射主节点的数据。主节点记录在其上的全部操做。
从节点按期轮询主节点获取这些操做,而后对数据副本执行这些操做。因为和主节点执行了相同的操做,从节点就能保持和主节点的数据同步。
9.4.1 oplog
主节点的操做记录成为polog(operation log)。oplog存储在一个特殊的数据库里,成为local。oplog就在其中的oplog.$main集合里面。oplog的每一个文档都表明主节点执行的一个操做。
myrepl:PRIMARY> db.oplog.$main.help();

查看oplog的内容:
myrepl:PRIMARY> use local;
switched to db local
myrepl:PRIMARY> show collections;
me
oplog.rs
replset.minvalid
slaves
startup_log
system.indexes
system.replset
myrepl:PRIMARY> db.oplog.rs.find();
{ "ts" : Timestamp(1393294283, 1), "h" : NumberLong(0), "v" : 2, "op" : "n", "ns" : "", "o" : { "msg" : "initiating set" } }
{ "ts" : Timestamp(1393294457, 1), "h" : NumberLong("-8949844291534979055"), "v" : 2, "op" : "i", "ns" : "test.test", "o" : { "_id" : ObjectId("530bfc79eee2c2ce39f9cd95"), "name" : "caoqing" } }
{ "ts" : Timestamp(1393294735, 1), "h" : NumberLong("677282438107403253"), "v" : 2, "op" : "i", "ns" : "test.test", "o" : { "_id" : ObjectId("530bfd8f3627cb16c15dcb32"), "name" : "xiaobao" } }
{ "ts" : Timestamp(1393295409, 1), "h" : NumberLong("5171944912929102944"), "v" : 2, "op" : "n", "ns" : "", "o" : { "msg" : "Reconfig set", "version" : 2 } }
myrepl:PRIMARY> 
文档包含的键以下:
ts 操做的时间戳。时间戳是一种内部类型,用于跟踪操做执行的时间。有4字节的时间戳和4字节的递增计数器构成。
op 操做类型,只有1字节代码。
ns 执行操做的命名空间。
o  进一步指定要执行操做的文档。
oplog只记录改变数据库状态的操做。oplog只是做为从节点和主节点保持数据同步的机制。
存储在oplog里的操做不是彻底和主节点的操做如出一辙的。这些操做在存储以前先作等幂变换,这些操做能够在从服务器端屡次执行,只要顺序是对的,就不会有问题。
oplog在固定集合中,不能保证oplog不超过预先设定的大小。须要在建立mongodb服务时指定--oplogSize,参数指定oplog的大小。
通常64bit-linux,分配5%的剩余空间,单位为MB。
9.4.2 同步
从节点第一次启动时,会对主节点数据进行完整的同步。从节点复制主节点上的每个数据,耗费资源大。同步完成后,从节点查询主节点的oplog,并执行这些操做,保证数据是最新的。
若是从节点的操做被主节点落下太远了,从节点就跟不上同步了,从节点发生宕机或者疲于应付读取时,就会出现这种状况,也会在执行完完整同步后出现这种状况,由于oplog可能已经回滚一圈了。
从节点跟不上同步后,复制就会停下,从节点须要从新作完整的同步。能够用{"resync" : 1}命令手动执行同步,也能够在启动从节点是使用--autoresync选项让其自动同步。从新同步代价高昂,尽可能避免,方法就是配置足够大的oplog。
9.4.3 复制状态和本地数据库
本地数据库用来存放全部内部复制状态,主节点和从节点都有。本地数据就是local,其内容不会被复制。能够确保一盒MongoDB数据库只有一个本地数据库。
本地数据库不限于存放MongoDB的内部状态。若是有不想复制的文档,也能够放在本地数据库的集合里。
主节点上的复制状态还包括从节点上的列表。这个列表存放在slaves集合中:
myrepl:PRIMARY> db.slaves.find();
{ "_id" : ObjectId("530bfbdc911eb0ac3bf2aa8b"), "config" : { "_id" : 1, "host" : "test03:10002" }, "ns" : "local.oplog.rs", "syncedTo" : Timestamp(1393295409, 1) }
从节点也在本地数据库中存放状态。在me集合中存放从节点的惟一标识符,在sources集合中存放源或节点的列表。
myrepl:SECONDARY> db.me.find();
{ "_id" : ObjectId("530bfbdc911eb0ac3bf2aa8b"), "host" : "test03" }
主节点和从节点都跟踪从节点的更新情况,这个是经过存放在"syncedTO"中的时间戳来完成的。
9.4.4 阻塞复制
开发者可使用getLastrror的'w'参数来确保数据的同步性。运行getLastError会进入阻塞状态,直到N个服务器复制了最新的写入操做为止。
检查本链接的上一次数据库操做的错误。
myrepl:PRIMARY> db.runCommand("getlasterror")
{
        "n" : 0,
        "lastOp" : Timestamp(0, 0),
        "connectionId" : 3525,
        "err" : null,
        "ok" : 1
}
指定"w"选项后,可使用"wtimeout"选项,表示以毫秒为单位的超时。
阻塞复制会致使写操做明显变慢,尤为是"w"的值比较大时。

9.5 管理
9.5.1 管理
MongoDB包含不少有用的管理工具,用以查看复制的状态。
经过 db.printReplicationInfo()命令查看oplog状态。
myrepl:PRIMARY>  db.printReplicationInfo();
configured oplog size:   997.7892578125001MB
log length start to end: 1126secs (0.31hrs)
oplog first event time:  Tue Feb 25 2014 10:11:23 GMT+0800 (CST)
oplog last event time:   Tue Feb 25 2014 10:30:09 GMT+0800 (CST)
now:                     Wed Feb 26 2014 02:07:23 GMT+0800 (CST)
输出信息包括oplog日志的大小,操做日志记录的起始时间。
查看从库同步状态。
myrepl:PRIMARY> db.printSlaveReplicationInfo();
source:   test03:10002
         syncedTo: Tue Feb 25 2014 10:30:09 GMT+0800 (CST)
                 = 56533 secs ago (15.7hrs)
source:   test01:10003
         no replication info, yet.  State: ARBITER
输出信息包括从库的主机名,port信息等。
9.5.2 变动oplog的大小
若是发现oplog大小不合适,最简单的方法就是停掉主节点,删除local数据库的文件,用心的设置从新启动。
# rm -rf /data/node2/local*
为大型的oplog预分配空间很是耗费时间,且可能致使主节点停机时间增长,尽量的手动预分配数据文件。
9.5.3 复制的认证问题
若是在复制中使用了认证,还须要作些配置,使得从节点能够访问俄主节点的数据。在主节点和从节点都须要在本地数据库增长用户,每一个节点的用户名和口令相同。
从节点链接到主节点是,会用存储在local.system.users中的用户认证。最早尝试"repl"用户,若是没有,则用local.system.users中的第一个可用用户。
相关文章
相关标签/搜索