Postgresql BDR 更新更新冲突检测

Tun*_*ble 1 postgresql postgresql-bdr

显然只有行的新值在更新时在节点之间发送。那么怎么会有可检测的冲突呢?节点 A 从节点 B 接收更新,从节点 C 接收不同的更新。A 怎么知道发生了冲突?B 和 C 更新不是以某种明确定义的顺序进行的?

“last-update-wins”是什么意思?更新有时间戳吗?在哪种情况下它依赖于同步时钟?或者它只是意味着最后一次到达节点 A 的更新会获胜?

概括

Cra*_*ger 6

手册对此进行了大量讨论。

是的,提交带有时间戳。冲突解决使用提交时间戳,而不是单个元组插入/更新/删除的时间戳。请参阅track_commit_timestampspostgres 文档。

在您的示例中,节点 A 可能会也可能不会看到冲突。如果 B 的更新首先发生(根据 B 的时钟),并且 B 的更新在 C 之前到达 A,则 A 不会看到冲突。但是,如果 C 的更新在 B 之前到达 A,并且 B 发生得更早,那么当 B 的更新到达 A 时,A 需要知道丢弃它,因为它已经应用了更新的更新。否则它会变得与 B 的状态不一致(它将在收到 C 的更新时应用 C 的更新)和 C(它用自己的较新的更新覆盖了 B 的旧更新)。

因为它是一个松散耦合的复制系统,没有全局锁管理器、快照管理器、事务管理器等,所以没有一个“现在”的概念,也没有一个全局一致的状态。或者至少由于延迟和复制滞后它是模糊的。您会看到这样的系统(有些不正确)被称为“AP”,因为CAP的“可用性”和“分区容限” 。它是一个最终一致的系统,其节点间保证比股票 PostgreSQL 更弱。您可以同时UPDATE在两个不同的节点上创建一个元组。您可以INSERT将相同的键放入两个不同节点上的 -UNIQUE约束列(或相同PRIMARY KEY)中。等等。

当节点复制和同步时,需要解决这些冲突,以便所有节点具有相同的结果并且不会导致错误。我们不能仅仅因为 xacts 已经在它们的原始节点上提交而出现 ERROR,而我们无法“取消提交”它们;即使我们这样做了,我们也不应该这样做,因为本地节点上的其他 xacts 可能已经使用这些提交的更改作为其他计算的输入,所以我们被它们困住了。

所以我们必须做一些事情,比如选择两个插入中最新的。我们为此使用提交的时间戳。它通过协议消息发送,并由 postgres 记录以用于本地提交。

旧行值对此无关紧要。例如,我们只关心某个对等节点更新了表“x”,但我们最近更新了它,因此我们假设应该保留较新的更新。否则,当我们的更改复制到对等节点时,它会应用我们的更改,如果我们也应用了他们的更改,我们将有不同的最终状态。为了获得一致的结果,我们必须放弃他们的变化。

类似地,如果插入了两个新行,我们必须始终选择其中之一以在它们在同一个 PK 上发生冲突时保留。我们根据记录的提交时间戳选择两行中最新的。

所以是的,它依赖于时钟。时钟不必完全同步,但大致正确是可取的,否则在解决冲突时可能会用明显较旧的元组覆盖较新的元组,即选择错误的“最后”更新。手册应该更多地讨论时钟同步。


在某些情况下,可以访问旧行值会很好,但不会让您确定正确的结果是什么。您仍然必须选择多个可能的“正确”结果之一。考虑一下:

给定的表

CREATE TABLE x (
  id integer primary key,
  y integer not null
);

INSERT INTO x (id, y) VALUES (1, 1);
Run Code Online (Sandbox Code Playgroud)

两个节点运行

UPDATE x SET y = y + 1 WHERE id = 1;
Run Code Online (Sandbox Code Playgroud)

同时。两者都会产生一个新的带有 value 的本地行y = 2。现在每个都复制到另一个。

如果我们知道旧值是 1 而新值是 2,我们可能会说“好吧,呃,两个节点上的正确结果都是 3”。

但是我们无法知道一个或两个节点没有运行

UPDATE x SET y = 2;
Run Code Online (Sandbox Code Playgroud)

而不是y = y + 1. 所以它可能希望最终结果是 2,而不是 3。唯一知道的方法是了解应用程序逻辑。BDR 不能仅根据新旧元组进行区分。

所以我们真的不需要旧的元组值。