1>、以单机模式启动成员:许多维护工作不能再备份节点上进行(因为要执行写操作),也不能在主节点上进行。单机模式(standalone mode)启动服务器,这是指要重启成员服务器,让它成为一个单机运行的服务器,而不再是一个副本集成员。在以单机模式启动服务器之前,先看一下服务器的命令行参数:
>db.serverCmdLineOpts()
{
"argv":["mongod","-f","/var/lib/mongod.conf"],
"parsed":{
"replSet":"mySet",
"port":27017,
"dbpath":"/var/lib/db"
},
"ok":1
}
如果要对这台服务器进行维护,可以重启服务器,重启时不使用replSet选项。这样它就会成为一个单机的mongod,可以对其进行读和写。不希望副本集中的其他服务器联系到这台服务器,,所以可以让它监听不同的端口,这样副本集的其他成员就找不到它了。最后保持dbpath的值不变,因为重启后台要对这台服务器的数据做一些操作。可以用如下参数启动服务器:$ mongod --port 30000 --dbpath /var/lib/db。这样这台服务器已经在单机模式中运行了,监听这30000端口的连接请求。副本集中的其他成员仍然会试图连接到它的27017端口。当在这台服务器上执行完维护工作之后,可以以最原始的参数重新启动它,启动之后,它会自动与副本集中的其他成员进行同步,将维护期间落下的操作全部复制过来。
2>、副本集配置:副本集配置总是以一个文档的形式保存在local.system.replSet集合中,副本集中所有成员的这个文档都是相同的。绝对不要使用update更新这个文档,应该使用rs辅助函数或者replSetReconfig命令修改配置副本集配置。(1)、创建副本集:创建副本集的步骤很简单,首先启动所有成员服务器,然后使用rs.initiate命令将配置文件传递给其中一个成员:
var config={
"_id":setName,
"members":[
{"_id":0,"host":host1},
{"_id":1,"host":host2},
{"_id":2,"host":host3}
]}
>rs.initiate(config)。
应该总是传递一个配置对象给rs.initiate,否则MongoDB会自动生成一个针对单成员副本集的配置,其中的主机名可能不是你希望的。只需要对副本集中的一个成员调用rs.initiate就可以了,收到initiate命令的成员会自动将配置文件传递给副本集中的其他成员。(2)、修改副本集成员:向副本集中添加新成员时,这个新成员的数据目录要么是空的(在这种情况下,新成员会执行初始化同步),要么新成员拥有一份其他成员的数据副本。添加新成员:rs.add("spock:27017"),也可以以文档的形式为新成员指定更复杂的配置:rs.add({"_id":5,"host":"spock:27017","priority":0,"hidden":true}),从副本集中移除:rs.remove("spock:27017"),可以通过re.reconfig修改副本集成员的配置,修改副本集成员配置时,有几个限制:不能修改成员的"_id"字段;不能将接收rs.reconfig命令的成员(通常是主节点)的优先级设为0;不能将仲裁者成员变成非仲裁者成员,反之亦然;不能将"buildIndexes":false的成员修改为true。可以修改成员的"host"字段,这意味着,如果副本集成员指定了不正确的主机名(比如使用了公网IP而不是内网IP),之后可以重新修改成员的主机名。例如:var config=rs.config(),config.members[0].host="spock:27017",rs.reconfig(config)。修改其他选项的方式也是一样的:使用rs.config得到当前配置文件,修改配置文件,将修改后的配置文件传递给rs.reconfig就可以了。(3)创建比较大的副本集:副本集最多只能拥有12个成员,其中只有7个成员拥有投票权。这是为了减少心跳请求的网络流量(每个成员都要想其他所有成员发送心跳请求)和选举花费的时间。如果要创建7个以上成员的副本集,只有7个成员可以拥有投票权,需要将其他成员的投票数量设置为0:rs.add({"_id":7,"host":"server-7:27017","votes":0}),这样可以阻止这些成员在选举中投主动票,虽然它们仍然可以投否决票。应该尽量避免修改成员的投票数量,投票可能会对选举和一致性产生怪异的、不直观的影响,应该只在创建包含7个以上成员的副本集或者是希望阻止自动故障转移时,使用"votes"选项。如果希望某个成员可以优先被选举为主节点,应该使用优先级。(4)、强制重新配置:如果副本集无法再达到“大多数”要求的话,那么他就无法选举出新的主节点,这时你可能会希望重新配置副本集,在这种情况下,可以在备份节点上调用rs.reconfig强制重新配置(force reconfigure)副本集。在shell中连接到一个备份节点,使用force选项执行rs.reconfig命令:rs.reconfig(config,{"force":true})。强制重新配置与普通的重新配置要遵守同样的规则:必须使用正确的reconfig选项将有效的、格式完好的配置文件发送给成员。force选项不允许无效的配置,而且只允许将配置发送给备份节点。强制重新配置会跳过大量的数值直接将副本集的version设为一个比较大的值。备份节点收到新的配置文件之后,就会修改自身的配置,并且将新的配置发送给副本集中的其他成员。副本集的其他成员收到新的配置文件之后,会判断配置文件的发送者是否是它们当前配置中的一个成员,如果是,才会用新的配置文件对自己进行重新配置。所以,如果新的配置会修改某些成员的主机名,应该将新的配置发送给主机名不发生变化的成员。如果新的配置文件修改了所有成员的主机名,应该关闭副本集的每一个成员,以单机模式启动,手动修改local.system.replset文档,然后重新启动。
3>、修改成员状态:未进行维护或响应加载,有多种方式可以手动修改成员的状态,注意,无法强制将某个成员变成主节点,除非对副本集做适当的配置。(1)、把主节点变为备份节点:可以使用setpDown函数奖主节点降级为备份节点,rs.setpDown()这个命令可以让主节点退化为备份节点,并维持60秒。如果这段时间内没有新的主节点被选举出来,这个节点就可以要求重新进行选举。如果希望主节点退化为备份节点并持续更长(或者更短)的时间,可以自己制定时间(以秒为单位):rs.setpDown(600)//10分钟。(2)、阻止选举:如果需要对主节点做一些维护,但是不希望这段时间内将其他成员选举为主节点,那么可以再每个备份节点上执行feeeze命令,以强制它们始终处于备份节点状态:rs.freeze(10000),这个命令也会接受一个以秒表示的时间,表示在多长时间内保持备份节点状态。维护完成之后,如果想"释放"其他成员,可以再次执行freeze命令,将时间指定为0即可:rs.freeze(0),这样,其他成员就可以在必要时申请被选举为主节点。也可以在主节点上执行rs.freeze(0),这样可以将退位的主节点重新变为主节点。(3)、使用维护模式:当在副本集成员上执行某个非常耗时的操作时,这个成员就进入维护模式(maintenance mode):强制成员进入RECOVERING状态。有时,成员会自动进入RECOVERING状态,这样就不会有请求发送给这个成员,客户端会停止从这个成员读取数据(如果之前有从这个成员读数据的话),这个成员也不能在作为复制源。可以看通过执行replSetMaintenanceMode命令强制一个成员进入维护模式:db.adminCommand({"replSetMaintenanceMode":true})。如果一个成员远远落后于主节点,你不希望它继续处理请求时,可以强制让这个成员进入维护模式。将成员从维护模式中恢复,可以使用如下命令:db.adminCommand({"replSetMaintenanceMode":false})。
4>、监控复制:监控副本集的状态非常重要,不仅要监控是否所有成员都可用,也要监控每个称成员处于什么状态,以及每个成员的数据新旧程度。1、获取状态:replSetGetStatus返回副本集中每个成员的当前信息,该命令还有一个对应的辅助函数rs.status()。~stateStr:用于描述服务器状态的字符串;uptime:从成员可达一直到现在所经历的时间,单位是秒;对于“self”成员,这个值是从成员启动一直到现在的时间;~optimeDate:每个成员的oplog中最后一个操作发生的时间(即操作被同步过来的时间),这里的状态是每个成员通过心跳报告上来的状态,所以optime跟实际时间可能会有几秒钟偏差; ~lastHeartbeat:当前服务器最后一次收到其他成员心跳的时间;~pingMs:心跳从当前服务器到达某个成员所花费的平均时间,可以根据这个字段选择从哪个成员进行同步;~errmsg:成员在心跳请求中返回的状态信息,这个字段的内容通常只是一些状态信息,而不是错误信息。2、复制图谱:如果在备份节点上运行rs.status(),输出信息中会有一个名为"syncingTo"的顶级字段,用于表示当前成员正在从哪个成员处进行复制,如果在每个成员上运行replSetGetStatus命令,就可以弄清楚复制图谱(replication graph)。MongoDB根据ping时间选择同步源。一个成员向另一个成员发送心跳请求,就可以知道心跳请求所耗费的时间。MongoDB维护着不同成员间请求的平均花费时间,选择同步源时,会选择一个离自己比较近而且数据比自己新的成员。因此,如果在备份数据中心添加一个新成员,它很可能会从与自己同在一个数据中心内的其他成员处复制,而不是从位于另一数据中心的主节点处复制。自动复制链(automatic replication chaining):复制链越长,将写操作复制到所有服务器所花费的时间就越长,复制链中的每个备份节点都要比它前面的备份节点稍微落后一点,只要出现这种状况,可以用replSetSyncFrom(或者对应的辅助函数rs.syncFrom())命令修改成员的复制源进行修复。连接到需要修改复制源的备份节点,运行这个命令,为其制定一个复制源:secondary.adminCommand({"replSetSyncFrom":"server-x:27017"})。3、复制循环:如果复制链中出现了环,那么就称为发送了复制循环。因为复制循环中的成员都不可能成为主节点,所以这些成员无法复制新的写操作,就会越来越落后。另一个方面,如果每个成员都是自动选取复制源,那么复制循环是不可能发生的。使用replSetSyncFrom强制为成员设置复制源时,就可能会出现复制循环。在手动修改复制源时,应该仔细查看rs.status()的输出信息,避免造成复制循环。4、禁用复制链:当一个备份节点从另一个备份节点(不是主节点)复制数据时,就会形成复制链。可以禁用复制链,强制要求每个成员都从主节点进行复制,只需要将"allowChaining"设置为fasle即可(如果不指定这个选项,默认是true):
>var config = rs.config()
>//如果设置子对象不存在,就自动创建一个空的
>config.settings = config.settings || {}
>config.settings.allowChaining = false
>rs.reconfig(config)
将allowChaining设置为false之后,所有成员都会从主节点复制数据。如果主节点变得不可用,那么各个成员就会从其他备份节点处复制数据。5、计算延迟:延迟(lag)是指备份节点相对于主节点的落后程度,是主节点最后一次操作的时间戳与备份节点最后一次操作的时间戳的差。跟踪复制情况的一个重要指标是备份节点与主节点之间的延迟程度。可以使用rs.status()查看成员的复制状态,也可以通过在主节点上执行db.printReplicationInfo(),或者在备份节点上执行db.printSlaveReplicationInfo()快速得到一份摘要信息(这两个都是db函数,不是rs的)。在备份节点上执行db.printSlaveReplicationInfo(),可以得到当前成员的复制源,以及当前成员相对复制源落后程度等信息。副本集成员的延迟是相对于主节点来说的,而不是表示需要多长时间才能更新到最新。6、调整oplog大小:oplog中第一条操作与最后一条操作的时间差就是操作日志的长度。可以将主节点的oplog长度看作维护工作的时间窗,每一个可能成为主节点的服务器都应该拥有足够大的oplog,以预留足够的时间窗用于进行维护。如果要增加oplog大小,可以按照如下步骤:(1)、如果当前服务器是主节点,让它退位,以便让其他成员的数据能够尽快更新到与它一致;(2)、关闭当前服务器;(3)、将当前服务器以单机模式启动;(4)、临时将oplog中的最后一条insert操作保存到其他集合中;(5)、删除当前oplog(删除命令:db.oplog.rs.drop());(6)、创建一个新的oplog(命令:db.createCollection("oplog.rs",{"capped":true,"size":10000}));(7)、将最后一条操作记录写会oplog;(8)、最后,将当前服务器作为副本集成员重新启动。通常不应该减小oplog的大小。7、从延迟备份节点中恢复:假设有人不小心删除了一个数据库,幸好你有一个延迟备份节点,现在,需要放弃其他成员的数据,明确将延迟备份节点指定为数据源,有几种方法可以使用:最简单做法:1关闭所有其他成员;2删除其他成员数据目录中的所有数据,确保每个成员(除了延迟备份节点)的数据目录都是空的;3重启所有成员,然后他们会自动从延迟备份节点中复制数据。这种方式非常简单,但是,在其他成员完成初始化同步之前,副本集中将只有一个成员可用(延迟备份节点)而且这个成员很可能会过载。根据数据量的不同,第二种方式可能更好,也可能更差:1关闭所有成员,包括延迟备份节点;2删除其他成员(除了延迟备份节点)的数据目录;3将延迟备份节点的数据文件复制到其他服务器;4重启所有成员。8、创建索引:如果想主节点发送创建索引的命令,主节点会正常创建索引,然后备份节点在复制"创建索引"操作时也会创建索引。这是最简单的创建索引的方式,但是创建索引时一个需要消耗大量资源的操作,可能会导致成员不可用,如果所有备份节点都在同一时间开始创建索引,那么几乎所有成员都会不可用,一直到索引创建完成。因此,可能会希望每次只在一个成员上创建索引,以降低对应用程序的影响。步骤如下:1关闭一个备份节点服务器;2将这个服务器以单机模式启动;3在单机模式下创建索引;4索引创建完成之后,将服务器作为副本集成员重新启动;5、对副本集中的每个备份节点重复以上步骤。应该根据自己的实际情况选择一个对生成系统影响最小的方式:在主节点上创建索引,如果系统会有一点负载比较小的“空闲期”,那会是非常好的创建索引的时机,也可以修改读取首选项,在主节点创建索引期间,将读操作发送到备份节点上。9、主节点如何跟踪延迟:作为其他成员的同步源的成员会维护一个名为local.slaves的集合,这个集合中保存着所有正从当前成员进行数据同步的成员,以及每个成员的数据新旧程度。如果使用w参数执行查询,MongoDB会根据这些信息确定是否有足够多、足够新的备份节点可以用来处理查询。local.slaves集合实际上是内存中数据结构的“回声”,所以其中的数据可能会有几秒钟的延迟。连接到一个成员,然后查询local.me集合可以知道一个成员的标识符(db.me.findOne())。如果多台服务器拥有相同的"_id",可以依次登录到每台服务器,删除local.me集合,然后重新启动mongod,启动时,mongod会使用新的"_id"重新生成local.me集合。如果服务器的地址发生了改变(假定"_id"没有变,但是主机名变了),可能会在本地数据库日志中看到键重复异常,遇到这种情况时,删除local.slaves集合即可。mongod不会清理local.slaves集合,所以,他可能会列出某几个月之前就不在把该成员作为同步源的服务器。由于MongoDB只是把这个集合用于报告副本集状态,所以这个集合中的过时数据并不会有什么影响。如果你觉得这个集合中的旧数据会造成困惑或者是过于混乱,可以将整个集合删除。几秒钟之后,如果有新的服务器将当前成员作为复制源的话,这个集合就会重新生成。如果备份节点之间形成了复制链,你可能还会注意到某个特定的服务器在主节点的local.slaves集合中有多个文档,这是因为,每个备份节点都会将复制请求转发给它的复制源,这样主节点就能够知道每个备份节点的同步源。这称为“影同步”,因为这些请求并不会要求进行数据同步,只是把每个备份节点的同步源报告给主节点。local数据库只用于维护复制相关信息,它并不会复制。因此,如果希望某些数据只存在于特定的机器上,可以将这些数据保存在local数据库集合中。
5>、主从模式(master-slave):在这种模式下,MongoDB不会做自动故障转移,而且需要明确声明主节点和从节点。有两种情形应该使用主从模式而不是副本集:需要多于11个备份节点,或者是需要复制单个数据库。除非迫不得已,否则都应该使用副本集。副本集更易维护,而且功能齐全。主从模式以后会被废弃,当副本集能够支持无线数据的成员时,主从模式很可能会被立即废弃。如果要将服务器设为主节点,可以使用--master选项启动服务器。对于从节点,有两个可用的选项:--slave和--source master。--source用于指定同步源的主机名和端口号。注意不要使用--replSet选项,因为现在是要设置主从模式,而不是副本集。假如有两条服务器,server-0和server-1,可以这么做:
$ # server-0
$ mongod --master
$
$ # server-1
$ mongod --slave --source server-0:27017
这样,主从模式就设置成功了,不需要其他的设置。在主节点执行的写操作,会被复制到从节点上。主从模式也可以用于复制单个数据库。可以使用--only选项选择需要进行复制的数据库。
$ mongod --slave --source server-1:27017 --only super-important-db,驱动程序不会自动将读请求发送给从节点,如果要从从节点读取数据,需要显示地创建一个连接到从节点的数据库连接。(1)、从主从模式切换到副本集模式:从主从模式切换到副本集模式,需要停机一段时间,步骤有:1停止系统的所有写操作(这非常重要,因为在主从模式下,从节点并不会维护一份oplog,所以他无法将升级期间落下的操作同步过来);2关闭所有的mongod服务器;3使用--replSet选项重启主节点,不在使用--master;4初始化这个只有一个成员的副本集,这个成员会成为副本集的主节点;5使用--replSet和--fastsync选项重启从节点。通常,如果向副本集中添加一个没有oplog的成员,这个成员会立即进入完全的初始化同步过程,fastsync选项用于告诉新成员不会担心oplog的问题,直接从主节点最新的操作开始同步即可;6使用rs.add()将之前的从节点加入副本集;7对每个从节点,重复第5和第6步;8当所有从节点都变为备份节点,就可以开启系统的写功能了;9从配置文件、命令行别名和内存中删除fastsync选项。(2)、让副本集模仿主从模式的行为:通常你会希望主节点长时间可用,因此,万一主节点不可用,应该允许自动故障转移。但是,对于某些副本集,你可能会要求手动选择新的主节点,不允许进行自动故障转移。这样的话,副本集的行为就跟主从模式一样了。为了实现这个目的,需要重新配置副本集,将所有成员(除主节点之外)的priority和votes设为0。这样一来,如果主节点挂了,不会有任何成员寻求被选举为主节点。另外,如果有备份节点挂了,主节点仍然会一直保持主节点状态,不会退位。如果主节点挂了,管理员必须手动选出新的主节点。如果要手动将某个备份节点提升为主节点,首先要连接到这个备份节点,然后执行强制重新配置,将他的priority和votes修改为1,同时将先前的主节点priority和votes修改为0。