mongo——复制

复制

复制是跨多个mongodb服务器(节点)分布和维护数据的方法。mongodb可以把数据从一个节点复制到其他节点并在修改时进行同步。这种类型的复制通过一个叫可复制集的机制提供。集群中的节点配置为自动同步数据,并且在服务器出错时自动灾备。mongodb也提供对于旧的复制方法的支持。这个旧方法叫做主从模式,现在已经过时了,但是主从复制仍然可以在mongodb3.0里使用。两种方法类似,主节点接受所有的写请求,然后所有的从节点读取,并且异步同步所有的数据。

主从复制和可复制集群使用了相同的复制机制,但是可复制集群额外增加了自动化灾备机制:如果主节点宕机,无论什么原因,其中一个从节点会自动提升为主节点。除此之外,可复制集群还提供了其他改进,比如更容易恢复和更复杂地部署拓扑网络。因此我们很少再用简单的主从复制机制。可复制集群是推荐的生产环境下的复制策略。

复制的缺点:回滚机制,在可复制集群里,数据并非正在地被提交,直到它被写入大多数集群的节点中,这指的是50%上的服务器。因此,如果可复制集群只有两个服务器,就意味着每一服务器可以停机。如果主节点在复制数据之前停机,其他成员会继续接受写入,而且任意未复制的数据必须回滚,意味着他不能被读取。

为什么复制很重要

所有的数据库都无法摆脱环境故障的影响

1.应用程序和数据库之前的连接丢失了

2.计划内停止阻止了服务器按照预期上线

复制的使用场景和限制

复制首先是为冗余设计的。它可以确保从节点与主节点的数据同步。这些复制节点可以和主节点位于一个数据中心或者为了安全可以分布于其他数据中心。

复制是异步的,任何网络延迟和分区都不会影响主节点的性能。

作为另外一种形式的冗余,复制节点也可以延迟某个固定的时间后执行。这防止了用户无意删除集合或者应用程序与数据库冲突的情况。通常,这些操作将会被立即复制;延迟复制可以给管理员足够的时间来做出响应并保存数据。

另外一个复制的使用情况是灾备,我们希望系统是高可用的,但是只有在冗余节点时才可以使用,并且在紧急情况下切换这些节点。mongodb的可复制集群让这一切都可以自动切换。

除了数据冗余和灾备,复制也简化了维护工作,通常通过允许管理员在从节点而不是主节点上运行命令。例如,通常的经验是在从节点上运行备份命令来缓解对于主节点的压力,避免宕机。构建大型索引是另外一个例子。因为所有构建非常昂贵,我们可以在从节点上优先构建,然后切换主从节点的角色,再次在新的从节点上构建索引。

最后,复制可以允许我们从节点上平衡读写压力。对于读取压力超大的应用系统,这个是最简单的解决办法,或者如果你选择最原始的做法,可以伸缩mongodb数据库。但是对于所有的承诺,可复制集群在以下情况下无能为力:

1.现有的硬件无法处理工作负荷,例如,我们前面章节提到的工作集,如果数据集超过了可用的内存大小,那么随机读取从节点就不会像我们期望的那样改善性能。从可复制集群从节点读数据可以增加IOPS数量,但是100-200IOPS可能无法解决性能问题,特别是同时写入并消耗部分IO数量的时候,此时,分片可能是最好的选择。

2.写入和读取的比例超过50%

3.应用程度需要一致性读取。从节点异步复制,因此不能确保反映最新主节点的写入数据。在极端的糟糕的情况下,从节点可能延迟几个小时。

可复制集

可复制集是推荐使用的mongodb复制策略。

可复制集群工作原理

可复制集群依赖两个基本的机制:oplog和heartbeat。oplog允许复制数据,heartbeat监控状态并触发灾备。

oplog

mongodb的复制机制依赖oplog。oplog是个盖子集合,它存在于每个复制节点的local数据库里,而且记录了所有的数据变化。每次客户端写入主节点的数据,包含足够重生信息的项目就会添加到主节点的oplog中。一旦写入的数据被复制到某个从节点,从节点的oplog也会存储这个写入请求的记录。每个oplog项目通过bson的时间戳来区分,并且所有的从节点使用时间戳来跟踪它们应用的最新项目。

local数据库存储了所有的可复制集群中的元数据和oplog操作日志。自然地,这个数据库不是复制的数据库本身,因此它有自己的名字,local数据库中的数据在本地节点被认为是唯一的,而且不能重复的。

如果检查local数据库,就会看到名为oplog.rs的集合,这就是可以复制存储oplog的地方。我们也会看到一些其他的集合,以下是完整的输出信息。

replset.minvalid包含可复制集成员最初同步的信息.并非所有的mongodb服务器都有replset.minvalid集合

system.replset存储了可复制集的配置文档。

me和slaves用来实现写关注点

system.indexes是标准的索引容器。

第一个字段ts存储的是项目的bson时间戳。时间戳包含2个部分:第一个部分表示从纪元(1970-01-01 00:00:00UTC)开始的秒数。第二个部分表示计数器的值,这里是1。使用时间戳查询,需要显示指定一个时间戳对象。所有的驱动都有自己的bson时间戳构造函数,而且JavaScript也有。

回到oplog项目,op字段指定opcode操作代码。它可以告诉从节点oplog项目表示的操作。这里我们看到的是i,表示插入操作。op后面是ns,它可以用来定义命名空间(数据库和集合),然后小写的字母o表示插入操作包含了一个查询文档的副本。

当我们查看oplog项目时,可能会注意到影响多个文档的操作会被单独记录日志。对于多个更新或者大量的删除,每个受影响的文档都会单独创建oplog文档。例如,假如我们要在集合中插入更多的Dickens的书。

现在集合中有4本书,我们来更新作者信息,添加作者的名字:

它是怎么出现在oplog中的呢?

正如你看到的,每个更新的文档都会有自己的oplog记录。这个过程是更通用的主从复制策略的一部分,用来确保主从节点一直有相同的数据。要保证这一点,每个应用的操作必须是幂等的——它无法干涉应用了多少次oplog项目。但是结果必须相同。其他多文档的操作,比如删除,将会出现相同的行为。我们可以尝试不同的操作来看看oplog的最终记录。

理解复制机制剩下的最后一点是,主节点追踪自己的oplog的步骤。答案是像从节点一样保存了oplog。这是对于主从复制的一大改进,所以值得花时间来研究底层实现。

想象一下,在主节点上写了个数据。接下来会发生什么?一开始,写操作会被记录并添加到主节点的oplog里。同时,所有的从节点都会复制主节点的oplog。当某个从节点准备更新自己的数据时,它会做3件事情。首先,它会查看自己oplog里最新记录的时间戳。然后,它会查询主节点的oplog,查询所有时间戳大于自己当前时间戳的oplog记录。最后,它写入数据,并添加每个操作日志到自己的oplog里。这意味着,假如出现故障,任意的提升为主节点的从节点都会有一个oplog,这样其他的从节点可以复制。这个特性本质上支持了可复制集的故障恢复功能。

从节点使用了长轮询来立即应用从节点复制的oplog记录。长轮询意味着从节点向主节点建立了个长连接。当主节点接受修改时,它会立即通知从节点。因此,从节点将几乎可以做到实时更新。当它们落后时,因为网络分区或者从节点维护,每个从节点里的oplog可以用来监控任意落后的复制。

停止复制

如果从节点无法在主节点oplog找到同步的日志记录点,就会永久停止复制。发生这种问题时,我们会看到从节点日志里有这样的异常信息:

回忆一下,oplog是个盖子集合。这意味着oplog只能存储固定数量的数据。如果从节点离线一段时间,oplog可能无法存储这段时间里的所有改变记录。一旦从节点无法从主节点找到同步的oplog点,就无法确定从节点是主节点完美复制集合了。

因为唯一的停止复制的补救措施就是完整地重新同步主节点的数据,肯定想避免这种问题。因此,需要去监控从节点的延迟,而且必须有足够大的oplog空间。

复制集oplog的大小

oplog是个盖子集合,因此,mongodb2.6不允许在创建后再修改其大小。所以非常重要的是开始就仔细选择oplog的大小。在mongodb3。0里,我们可以修改oplog的大小。操作步骤就是,停止mongodb实例,然后作为独立节点启动,修改oplog大小,重新启动成员。

心跳和故障转移

可复制集心跳便于实现选举和灾备。默认情况下,每个可复制集成员会每隔2秒ping一次索引的其他成员。这样,系统就可以判断自己的健康状态。当运行rs.status()时,查看每个节点的最新心跳和状态(1 健康,0无应答)。

只要每个节点仍然健康并且响应,可复制集就会继续正常工作。但是如果某个节点无法响应,就可能会采取行动了。每个可复制集都要确保一直只有一个主节点存在。这只是在大多数节点可用的时候。例如,如果关闭了某个从节点,但是大多数节点还在,那么可复制集就不会改变,而是简单地等待从节点回来,重新上线。如果杀死了主节点,则大多数节点还存在,但是没有主节点,因此从节点会自动提升为主节点。如果多于一个从节点,则最新的从节点将会被选中。

但是也有其他可能的场景存在。想象下从节点和裁判都被杀死的情况。选中的主节点还在,但是没有其他节点——最初的节点只有一个是健康状态。此时,我们会在主节点的日志里看到如下的信息

由于没有从节点,主节点降级为从节点。这看起来有点可笑,但是思考一下如果此节点依然是主节点会发生什么问题?如果是网络分区等原因导致心跳失败,则其他节点就会仍然在线。如果裁判和从节点仍然正常工作,并且可以看到彼此,则根据大多数原则,剩余的从节点会变成主节点。

如果主节点不退位,那么就会遇到一个尴尬的情况:一个可复制集有2个主节点,如果应用程序持续运行,它也许会向两台节点写入或者读取数据,肯定会造成不一致而且非常奇怪的行为。因此当主节点看不到其他成员时,它必须退位降级

 提交和回滚

从本质上讲,你可以一直向主节点写入数据,但是这些写入操作不会被提交,直到它们被复制给大多数从节点,那么什么是提交?

注意,对于mongodb单个文档的操作都是原子性的,但是对于多个文档的操作并非原子性的。想象一下前面提到的,假如我们在主节点插入了一些数据,但是由于某种原因没有及时复制到从节点(连接问题,从节点无法备份,从节点延迟等)。现在假设从节点突然提升为主节点,我们向新的主节点写入数据,但是最终旧的主节点又回来了,并且尝试从新的主节点复制数据。当旧的主节点中有一些写入数据在新主节点的oplog里不存在时,就会触发回滚。

在回滚里,所有没有复制给大多数节点的写入操作都会被取消。这意味着它们会被从从节点oplog和集合里删除。如果从节点已经注册了个删除操作,则该节点将会从其他复制集里查询该文档并恢复它。对于文档更新和删除集合的操作恢复机制是一样的。

还原的写操作被存储在相关节点数据目录的子目录里。对于每个回滚写入的集合,都会创建一个单独的bson文件,文件名包含回滚的时间。在需要恢复回滚文档的事件里,可以使用bsondump工具来检查bson文件,并且手动恢复他们,可以使用mongorestore。

如果必须恢复回滚数据,就会发现这是我们想要极力去避免的。幸运的是,某种程度上也是可以的。如果应用程序可以容忍写延迟,那么可以使用写关注点,这个概念在后面介绍,它可以确保写数据被复制到大多数的节点。聪明地选择写关注和监控复制延迟可以帮助我们减少回滚,或者避免他们。

读伸缩

对于读伸缩来说,复制数据库是很好的解决方案。如果单个服务器无法处理应用的读压力,就可以把查询请求路由到可复制集中的多台服务器上。

1.primary——这是默认的设置,表明只从可复制集主节点读取数据,因此一直是连续的。如果可复制集有问题,并且没有可用的从节点,就表示出现错误。

2.primaryPreferred——设置了此参数的驱动会从主节点读取数据,除非某些原因使主节点不可用或者没有主节点,此时它会从从节点读取数据。这种设置对于我们想确保读请求不会影响主节点的写入请求时非常有用。如果没有可用的从节点,读请求会抛出异常

3.secondary——这个设置告诉驱动应该一直从从节点读取数。这种设置对于我们想确保读请求不会影响主节点的写入请求时非常有用。如果没有可用的从节点,读请求会抛出异常。

4.secondaryPreferred——相比前面的设置,这个更加灵活。读请求会发送到从节点,除非没有从节点可用,此时会从主节点读取。

5.nearest——此配置的驱动会尝试从最近的可复制集成员节点读取数据,通过网络延迟判断。可以是主节点也可以是从节点。因此读请求只会发送给驱动认为最快通信的节点。

总结:

1.mongodb的生产环境部署时,数据保护应该使用可复制集,如果做不到这一点,就必须经常备份数据

2.可复制集应该包含至少3个成员,其中一个是裁判

3.数据不会提交,直到它被写入可复制集的大数据节点中。在故障的场景下,如果大多数成员存在,它们就继续接受写请求。没有达到大多数成员的写请求会被放入回滚数据目录下,并且必须手动处理。

4.如果可复制集从节点宕机一段时间,并且数据库修改没有记录到mongodb的oplog里,这个节点就没有办法及时填补数据,而且必须重新同步数据。为了避免这个问题,可以尝试最小化从节点的失败时间。

5.驱动的写关注点控制着在返回之前必须有多少个节点被写入。增加这个值可以增加持久性。对于正式的持久性,我们推荐设置为大多数成员以避免回滚场景,但这个方法会带来延迟成本。

6.mongodb允许我们通过读偏好和标签来控制更复杂的可复制集的读写行为,特别是在多个数据中心部署可复制集成员时。

发布了43 篇原创文章 · 获赞 37 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_28119741/article/details/103467596