数据库复制技术之二:多领导者复制


这是数据库数据复制技术续集,前文讨论了单领导者复制。现在让我们讨论一个替代方案,并探讨自己的挑战,并尝试确定使用它的方案。
考虑多领导方法的主要原因是解决了当我们只有一个领导节点时我们面临的一些问题。也就是说,我们有多个节点处理写入,这些写入可以由更靠近客户端的数据库执行。
如果您的应用程序需要处理大量写入,那么将该工作分成多个领导者可能是有意义的。此外,如果在非常远的数据库中写入的延迟价格太高,则每个位置可能有一个领导者(例如,一个在北美,一个在欧洲,另一个在亚洲)。
另一个好用例是当您需要支持脱机客户端时,可能正在写入自己的(领导者)数据库,并且一旦此客户端再次联机,这些写入需要与其余数据库同步。
多个领导者接受写入将面临的主要问题是你需要一些方法来解决冲突。例如,假设您有数据库约束以确保用户的电子邮件是唯一的。如果两个客户端写入两个尚未同步的不同领导者,则两个写入都将在各自的领导者中成功,但是当我们尝试复制该数据时,我们会遇到问题。让我们再谈谈这些冲突。

在这里,我们假设这些领导者异步复制数据,这就是我们可能发生冲突的原因。从理论上讲,你可以拥有多领导同步复制,但这并没有多大意义,因为你失去了让领导者独立接受写入的主要好处,而且可能只使用单一领导者复制。但是有一些项目,比如PgCluster,它们实现了多主同步复制,但是它们大多被放弃了,我不会在这里讨论这种类型的复制。

处理冲突
处理冲突的最简单方法是首先不要发生冲突。不是每个人都有幸能够做到这一点,但让我们看看如何实现这一目标。
让我们以一个管理公司的项目的案例为例。您可以确保将与美国办事处相关的项目中的所有更新发送给北美的领导者,并将所有欧洲项目写入欧洲的领导者。这样就可以避免冲突,因为对同一项目的写入将发送给同一个领导者。此外,如果我们假设更新这些项目的客户可能会在他们各自的办公室(例如,纽约办事处的人员将更新美国项目,这将被发送给北美的领导者),我们可以确保他们是访问地理位置靠近它们的数据库。
当然,这是一个非常极端的例子,并不是每个应用程序都能以这么简单的方式“分割”它的数据,但是要记住这一点。如果这符合你的情况,我们需要另一种方法来确保我们最终处于一致的状态。

我们不能让每个节点只按照他们看到的顺序应用写入,因为节点A可能首先接收更新设置foo=1然后接收另一个更新设置foo=2,而节点B以相反的顺序接收这些更新(请记住,这些消息正在通过网络并且可以无序地到达),如果我们盲目地应用它们,我们最终将在节点A实现foo=2和节点上B实现foo=1。这个不行。

一种常见的解决方案是为每次写入附加某种时间戳,然后只应用具有最高值的写入。这称为LWW(最后写入获胜)。正如我们之前所讨论的,使用这种方法我们可能会丢失数据,但这仍然被广泛使用。

请注意,物理时钟不可靠,使用时间戳时,您可能至少需要某种时钟同步,如 NTP

另一种解决方案是记录这些冲突,然后编写应用程序代码以允许用户稍后手动解决它们。在某些情况下,这可能不可行,例如我们之前的示例,其中包含电子邮件列的唯一约束。但是,在其他情况下,可能只是显示两个值并让用户决定应该保留哪个值以及哪个值应该被丢弃。

最后,一些数据库和复制工具允许我们编写自定义冲突解决代码。该代码可以在写入或读取时执行。例如,当检测到冲突时,可以使用冲突的值调用存储过程,并且它决定如何处理它们。这是写入 冲突解决方案。BucardoBDR是使用这种方法的工具的例子。

其他工具使用不同的方法,存储所有冲突的写入,并在客户端尝试读取该值时返回所有这些。然后,客户端负责决定如何处理这些值,并将其写回数据库。例如,CouchDB就是这样做的。

还有一个相对较新的数据结构系列,可以自动解决冲突。它们被称为无冲突复制数据类型或 CRDT,复制 维基百科 定义:

CRDT是一种数据结构,可以在网络中的多台计算机上进行复制,其中副本可以独立且同时更新,而无需在副本之间进行协调,并且在数学上始终可以解决可能导致的不一致问题。

不幸的是,这些数据结构的使用位置存在一些限制(否则我们的生活会太简单了,对吧?),据我所知,尽管有些CRDT已经实施,但它们仍然没有被广泛用于数据库中的冲突解决方案。

DDL复制
在多领导者场景中处理DDL(数据库结构的变化,如添加/删除列)也很棘手。从某种意义上说,这也是一个冲突问题,我们无法在其他节点仍在写入旧结构时更改数据库结构,因此我们通常需要获取全局数据库锁,等待所有挂起的复制发生,然后执行此DDL。与此同时,所有写入都将被阻止或失败。当然,具体细节将取决于所使用的数据库或复制工具,其中一些甚至不会尝试复制DDL,因此您需要以某种方式手动执行此操作,而其他工具将复制某些类型的DDL,但不会复制其他类型的DDL。 (例如,禁止重写整个表的DDL) BDR)。

关键是,当您有多个领导者时,复制DDL会涉及更多协调,因此在考虑此设置时也要记住这一点。

多领导设置的拓扑结构
我们可以在多领导者架构下使用几种不同类型的拓扑。拓扑定义了节点之间的通信模式,排列通信路径的不同方式具有不同的特征。
如果您只有两个领导者,则没有很多选项:节点A向节点发送更新B,节点B向节点发送更新A。当你有三个或更多的领导者时,事情开始变得更有趣。
常见的拓扑结构是让每个领导者将其更新发送给其他所有领导者。
这里的主要问题是消息可能无序到达。例如,如果一个节点A插入一行,然后节点B更新该行,但节点C 在插入之前收到更新,我们就会遇到问题。
这是一个因果关系问题,我们需要确保所有节点在处理update事件之前首先处理insert事件。有不同的方法可以解决这个问题(例如,使用逻辑时钟),但重点是:您需要确保您的数据库或复制工具实际上正在处理此问题,或者,如果不是,请注意这是可能发生的失败。

另一种方法是使用某些数据库调用起始拓扑 start topology的方法。
在这种情况下,一个节点接收更新并将其发送给其他所有人。通过这种拓扑结构,我们可以避免因果关系问题,但另一方面,引入单点故障。如果此中央节点终止,则复制将停止。在某些情况下,支付的价格很高。
当然,这只是两个例子,但想象力是你可以拥有的所有不同拓扑的极限,而且没有完美的答案,每个都有其优点和缺点。

数据库数据复制技术