注意: 我写一些短篇的博客已经有些日子了. 我认为是时候改变下了.我不确定这篇博客会带来什么影响,但应该会是很大的影响.请让我知道你觉得更好的方法和理由。 这个问题我已经想了几天了. 我试图想找出通用的复制解决方案能够应用到其他解决方案上去. 如果这个问题解决了,我们就能提供更多的功能组到更多的场景上. 但在之前,我们要谈谈怎么去实现复制, 哪些类型的复制. 我们假设有一个单独的数据库 (没分片,在不同的节点). 普通情况下, 将有下面的选项:
上面的是我接下来博客要谈的内容.对于这些内容的目的,他们是完全不想关的。 主/从模式是指这样一种情况,你只有一个主写节点和一个或多个从节点。这一模式的一大特点是你永远无法(至少在正常操作的情况下)对从节点做任何形式的更改。它们纯粹是用来读的,即使冒着损坏数据的风险切断它们与主节点的联系它们也不能变成可写的。 这种方法的一个常见的例子是日志传送。我将在后面详细讨论它,但是你可以看看其他类似系统的文档,将一个从节点变更为可写是一个绝对不凡的经历。得有一个很好的理由。 主/次级模式与主/从模式很类似,但在这种模式里我们可以选择一个次级节点成为主节点。只能有一个主服务器,但好处是允许有一种简单的方法来变更主节点。MongoDB就使用这样一个系统。 多重可写组合系统允许任何节点接受写操作,并且它会留意将变更分发到其他节点。它也需要处理冲突,不像目前提到的其他选择。拥有使两个用户在多个节点同时写入相同值的能力。然而,多重可写组合通常会对同伴节点进行假设。例如,它们会在同步时比较,并有一个单独的协议用来将新的在线节点加入到常规复制组合中。 多重主系统允许、接受并鼓励节点按需添加删除,它们假设会有写冲突,并有在运行的基础上解决冲突的需求。其他节点间没有相互同步的需求,它们通常“重新找主”到一个新的节点并开始复制它,这意味着你需要从一开始就复制所有数据。通常很希望到一个节点挂掉了,希望它在挂掉时已经完成所有变更,然后将它摘除。 让我们看看每一个实际执行的细节,包括一些例子,希望这能让我说得更清楚。
主/从通常是通过日志泊运来实现的。理解日志泊运的最简单方法是,主数据库会发送(很神奇,我们真的不太在乎这一点是怎样的)如何直接修改数据库文件的指令给从数据库。换句话说,从概念上讲,它会发送类似以下内容: 1: writep(fd1, 1024, new[]{ 17,85,124,13,86}, 5);2: writep(fd1, 18432, new[]{ 12,95,34,83,76,32,59}, 7);b 如你所想,这些是非常底层的修改。这个好处是非常易于捕捉和回放那些改变,而劣势是,你真地不能做任何其它事情。因为改变发生在堆栈的最底部,没有机会去运行任何各类的逻辑。我们只是写入到文件,如同主服务器所做的。 这就是为什么允许从节点写很难的最关键原因。当它产生任何独立的写操作时,它便冒着主节点也在写的风险,这样会产生数据冲突。这就是为什么如果你想切换主从你必需做一系列事情。你必须处理完这些麻烦来保证你不会有两端都写的场景发生。 一旦发生这种情况,你永远不不能再使两端同步了。这发生的几率很低。 增加一个新节点,反过来,这很容易。请务必保留过程,做一个完整的数据库备份并将它移动到另一个节点。然后开始传递日志。因为只有它们在同一个点开始时一切才能安全实施。 请注意,备份的这个版本在处理版本问题上很敏感。你不能在使用最底层存储的版本上做一丁点改变,因为那样可能会丢失一切。这个方法用于生成读复制很好用。事实上,这是大多数情况下使用的方法。 理论上你甚至可以用它来做故障转移,因为如果主节点宕掉了,从节点可以接受写。问题是你如何处理从节点以为主节点宕掉了,而主节点以为一切都正常这种情形。这种情况下你可能让两端都可写,而这将导致不能合并地情况。 理论上讲,因为它们有一个共同的根节点,你或许会觉得有一个引领者,并这样做了,但是这样会导致掉线服务器数据的丢失,或者你会没有可行方法取回的数据。这里我们记录的变化非常小,并且粒度太小以至于不允许你在提取变化信息方面做什么有用工作。
这实际上与日志传送方法非常类似,只是不发送底层的文件I/O操作,我们实际上发送的是更高层的命令。这就我们而言有相当多的好处。主服务器可以像如下一样发送日志: set("users/1", {"name": "oren" }); set("users/2", {"name": "ayende" }); del("users/1"); 在次级节点上执行这套指令将导致次级上结果相同的状态。不像日志传送那样,这实际上需要次级服务器进行工作,所以相比应用已经计算过的文件更新这样的代价更昂贵。 |