1>、复制:使用复制可以将数据副本保存到多台服务器上,使用mongodb的复制功能,即使一台或多台服务器出错,也可以保证应用程序正常运行和数据安全。在mongodb中,创建一个副本集之后就可以使用复制功能了。副本集是一组服务器,其中有一个主服务器(primary),用于处理客户端请求;还有多个备份服务器(secondary),用于保存主服务器的数据副本,如果主服务器崩溃了,备份服务器会自动将其中一个成员升级为新的主服务器。
2>、建立副本集:使用 --nodb选项启动一个mongo shell,这样可以启动shell但是不连接到任何mongod,然后通过执行下面的命令就可以创建一个副本集:replicaSet=new ReplSetTest({nodes:3}),这行代码可以创建一个包含三个服务器的副本集:一个主服务器和两个备份服务器,但是,在执行下面两个命令之前mongod服务器不会真正启动:启动副本集:replicaSet.startSet(),启动3个mongod进程;配置复制功能:replicaSet.initiate()。连接到运行在某个端口的mongod用如下命令:>conn=new Mongo("ip:port");>primaryDB=conn.getDB("db名");在连接到主节点的连接上执行isMaster命令,可以看到副本集的状态,查看当前的副本节点是否为主节点:primaryDB.isMaster();关闭主节点:primaryDB.adminCommand({"shutdown":1})。备份节点的数据可能会落后于主节点,可能没有最新写入的数据,所以备份节点在默认情况下会拒绝读取请求,以防止应用程序意外拿到过期的数据,因此,如果在备份节点上做查询,可能会得到一个错误提示,如果希望从备份节点读取数据,需要设置“从备份节点读取数据没有问题”标识,conn.setSlaveOk(),注意,slaveOk是对连接设置的,不是对数据库设置的。但是,不能对备份节点执行写入操作,备份节点只能通过复制功能写入数据,不接受客户端的写入请求。自动故障转移(automatic failover):如果主节点挂了,其中一个备份节点会自动选举为主节点,第一个检测到主节点挂了的备份节点会成为新的主节点。关闭副本集:replicaSet.stopSet()。
3>、配置副本集:在实际的部署中,需要在多台机器之间建立复制功能。假设已经有一个运行在server-1:27017上的单个mongod实例,其中已经有一些数据,首先为副本集选定一个名字,名字可以是任意UTF-8字符串,使用--replSet name选项重启servier-1。例如:$ mongod --replSet spock -f mongod.conf --fork,使用同样的replSet和表示符(spock)再启动两个mongod服务器作为副本集中的其他成员:$ ssh server-2,然后输入命令--replSet spock -f mongod.conf --fork,server-3同上。只有第一个副本集成员拥有数据,其他成员的数据目录都是空的,只要将后两个成员添加到副本集中,它们就会自动克隆第一个成员的数据。将replSet选项添加到每个成员各自有的mongod.conf文件中,以后启动时就会自动使用这个选项。现在有3个分别运行在不同服务器上的mongod实例,为了让每个mongod能够知道彼此的存在,需要创建一个配置文件,在配置文件中列出每一个成员,并且将配置文件发送给server-1,然后server-1会负责将配置文件传播给其他成员。在shell中创建一个配置文件:>config={"_id":"spock","members":[{"_id":0,"host":"server-1:27017"},{"_id":0,"host":"server-2:27017"},{"_id":0,"host":"server-3:27017"}]},外层"_id"值是启动时从命令行传递进来的副本集名称(spock),一定要保证这个名称与气动时传入的名称一致。config文档的剩余部分是一个副本集成员数组,其中每个元素都需要两个字段:一个唯一的数值类型的"_id"字段,和一个主机名。这个config对象就是副本集的配置。连接到主服务器(server-1),使用config对象对副本集进行初始化:>db=(new Mongo("server-1:27017")).getDB("test")//连接到主服务器,>rs.initate(config)//初始化副本集。server-1会解析这个配置对象,然后像其他成员发送消息。如果正在创建一个全新的副本集,可以将配置文件发送给副本集的任何一个成员;如果副本集中已经有一个有数据的成员,那就必须将配置文件发送给这个拥有数据的成员,如果拥有数据的成员不止一个,那么就无法初始化副本集。rs辅助函数:rs.initiate()命令中的rs是一个全局变量,其中包含与复制相关的辅助函数(可以执行rs.help()查看可用的辅助函数)。副本集内的每个成员都必须能够连接到其他所有成员。
4>、修改副本集配置:可以随时修改副本集配置,可以添加或者删除成员,也可以修改已有的成员。可以使用rs.add("server-4:27017")为副本集添加新成员,使用rs.remove("server-2:27017")从副本集中删除成员。重新配置副本集是,作为重新配置过程的最后一步,主节点会关闭所有连接,因此,shell中的连接会短暂断开,然后重新自动建立连接;重新配置副本集时,主节点需要先退化为普通的备份节点,以便接受新的配置,然后会恢复。可以在shell中执行rs.config()来查看配置修改是否成功。也可以修改副本集的现有成员,为了修改副本集的成员,可以在shell中创建新的配置文档,然后调用rs.reconfig()。例如:var config=rs.config(),config.members[1].host="server-2:27017",rs.reconfig(config)。对于复杂的数据集配置修改,rs.reconfig通常比rs.add和rs.remove更有用。
5>、设计副本集:副本集中很重要的一个概念是“大多数(majority)”:选择主节点时需要由大多数决定,主节点只有在得到大多数支持时才能继续作为主节点,写操作被复制到大多数成员时这个写操作就是安全的。大多数被定义为“副本中一半以上的成员”,如果副本集中有些成员挂了或者是不可用,并不会影响“大多数”,因为大多数是基于副本集的配置来计算的。假设有一个包含5个成员的副本集,其中3个成员不可用,仍然有2个可以正常工作,由于剩余的2个成员已经无法达到副本集“大多数”的要求,所以它们无法选举主节点。如果这两个成员中有一个是主节点,当它注意到无法得到“大多数”成员支持时,就会从主节点退位,这样由于副本集中只有少数成员可用,所有成员都将会变成备份节点。这种机制就可以避免出现多个主节点,从而导致副本集的数据发生混乱。通常只能有一个主节点,这对于副本集的配置是很重要的。如果副本集成员总数是偶数,成员平均分配到不同的网络中,任何一边都无法满足“大多数”的条件,下面是两种推荐的配置方式:第一种、将“大多数”成员放在同一个数据中心,如果有一个主数据中心,而且你希望副本集的主节点总是位于数据中心的话,这样的配置会比较好,只要主数据中心能够正常运转,就会有一个主节点,但是如果主数据中心不可用力,那么备份数据中心无法选举出主节点;第二种、在两个数据中心各自放置数量相等的成员,在第三个地方放置一个用于决定胜负的副本集成员,如果两个数据中心同等重要,那么这种配置会比较好,因为任意一个数据中心的服务器都可以找到另一台服务器以达到“大多数”,但是这样就需要将服务器分散到三个地方。mongodb只支持单一主节点,这样可以使开发更容易,但是当副本集被设为只读时,将导致程序暂时无法写入数据。选举机制:当一个备份节点无法与主节点连通时,它就会联系并请求其他的副本集成员将自己选举为主节点。其他成员会做几项理性检查:自身是否能够与主节点连通?希望被选举为主节点的备份节点的数据是否最新?有没有其他更高优先级的成员可以被选举为主节点?如果要求被选举为主节点的成员能够得到副本集中“大多数”成员的投票,它就会成为主节点。即使“大多数”成员中只有一个否决了本次选举,选举就会取消。如果成员发现任何原因,表明当前希望成为主节点的成员不应该成为主节点,那么它就会否决此次选举。希望成为主节点的成员(候选人)必须使用复制将自己的数据更新为最新,副本集中的其他成员会对此进行检查。复制操作是严格按照时间进行排序的,所以候选人的最后一条操作要比它能连通的其他所有成员更晚(或者与其他成员相等)。每个成员都只能要求自己被选举为主节点,不能推荐其他成员被选举为主节点,只能为申请成为主节点的候选人投票。
6>、成员配置选项:有时候并不希望每个成员都完全一样,可能希望让某个成员拥有优先成为主节点的权力,或者是让某个成员对客户端不可见,这样便不会有读写请求发送给它。在副本集配置的子文档中可以为每个成员指定这些选项(甚至更多)。选举仲裁者(arbiter):仲裁者的作用就是参与选举,仲裁者并不保存数据,也不会为客户端提供服务,它只是为了帮助具有两个成员的副本集能够满足“大多数”这个条件。由于仲裁者并不需要履行传统mongodb服务器的责任,所以可以将仲裁者作为轻量级进程,运行在配置比较差的服务器上。如果可能,应该将仲裁者放在单独的故障域中,与其他成员分开,这样它就可以以“外部视角”来看待副本集中的成员了。启动仲裁者与启动普通mongod的方式相同,使用"--replSet 副本集名称"和空的数据目录。可以使用rs.addArb()辅助函数将仲裁者添加到副本集中:rs.addArb("server-5:27017"),也可以在成员配置中指定arbiterOnly选项,例如:rs.add({"_id":4,"host":"server-5:27017","arbiterOnly":true}).成员一旦以仲裁者的身份添加到副本集中,它就永远只能是仲裁者,无法将仲裁者重新配置为非仲裁者。最多只能使用一个仲裁者,如果节点数是奇数,就不需要仲裁者。如果不知道应该将一个成员作为数据节点还是作为仲裁者时,应该将其作为数据节点。如果可能,尽可能在副本集中使用奇数个数据成员,而不要使用仲裁者。优先级:优先级用于表示一个成员渴望成为主节点的程度,优先级的取值范围可以是0~100,默认是1。将优先级设为0有特殊含义:优先级为0的成员永远不能够成为主节点,这样的成员称为被动成员(passive member)。拥有最高优先级的成员会优先选举为主节点(只要它能够得到集合中“大多数”的赞成票,并且数据是最新的)。设置优先级并不会导致副本集中选不出主节点,也不会使数据不够新的成员成为主节点(一直到它的数据更新到最新)。使用优先级时有一点需要注意:修改副本集配置时,新的配置必须要发送给在新配置下可能成为主节点的成员。因此,无法再一次reconfig操作中将当前主节点的优先级设置为0,也不能对所有成员优先级都为0的副本集执行reconfig,优先级的值只会影响副本集成员间相对优先级大小关系。隐藏成员:客户端不会向隐藏成员发送请求,隐藏成员也不会作为复制源(尽管当其他复制源不可用时隐藏成员也会被使用)。可以在他的配置中指定hidden:true。只有优先级为0的成员才能被隐藏(不能将主节点隐藏)。例如:var config=rs.config(),config.members[2].hidden=0,config.members[2].priority=0,rs.reconfig(config)。在执行rs.isMaster()可以查看是否隐藏。使用rs.status()和rs.config()能够看到隐藏成员,隐藏成员只对isMaster()不可见。要将隐藏成员设为非隐藏,只需将配置中的hidden设为false就可以了,或者删除hidden选项。延迟备份节点:可以使用slaveDelay设置一个延迟的备份节点,slaveDelay要求成员的优先级是0,延迟备份节点的数据会比主节点延迟指定的时间(单位是秒),这样如果有人不小心摧毁了你的主集合,还可以将数据从先前的备份中恢复过来。创建索引:有时,备份节点并不需要与主节点拥有相同的索引,甚至可以没有索引。如果某个备份节点的用途仅仅是处理数据备份或者是离线的批量任务,那么你可能希望在它的成员配置中指定"buildIndexs":false,这个选项也要求成员的优先级为0。这个选项可以阻止备份节点创建索引,这是一个永久项,指定了"buildIndexes":false的成员永远无法恢复为可以创建索引的“正常”成员。如果确实需要将不能创建索引的成员修改为可以创建索引的成员,那么必须将这个成员从副本集中移除,再删除它的所有数据,最后再将它重新添加到副本集中,并且允许它重新进行数据同步。