在 Postgres 上使用两阶段提交

Vic*_*tor 7 postgresql two-phase-commit

假设“db1”中有一个名为“t1”的表,“db2”中有一个名为“t2”的表,我需要在两个表上插入一条记录,否则会失败。

连接到 db1,我想我应该输入以下内容:

BEGIN;
PREPARE TRANSACTION 'pepe'; -- this says the manual that makes your transaction gets stored on disk, so what is the purpose if I can't use it from another database?)

insert into t1 (field) values ('a_value');

COMMIT PREPARED 'pepe'
Run Code Online (Sandbox Code Playgroud)

连接到db2,我猜:

BEGIN;
PREPARE TRANSACTION 'pepe'; -- this fails (the name of the transaction, what is the meaning, what is use for?)
 -- It complains about this "ERROR:  transaction identifier "pepe" is already in use"

insert into t2 (field) values ('another_value');

COMMIT PREPARED 'pepe'
Run Code Online (Sandbox Code Playgroud)

正如您可能看到的,我不明白如何在 Postgres 上使用两阶段提交。

概括

我不知道如何在同一 RDBMS 中的不同数据库上执行同步命令。

我在 Postgres 文档中读到,为了在两个或多个不相关的 Postgres 数据库之间同步工作,我们可以使用所谓的“两阶段提交”协议的实现。

所以我开始尝试看看人们如何在 Postgres 中实际使用它们,我没有看到任何实际的例子,最多我看到了一个人的这篇文章,他试图按顺序连接到不同数据库的几个 Postgres 客户端进行实验模拟并行运行的多个进程对多个数据库执行操作,这些数据库应以成功(所有提交)或失败结果(所有回滚)结束。

我见过的其他例子有:

决议(劳伦斯回答后)

连接到 db1,要执行的 SQL 行如下:

BEGIN;
-- DO THINGS TO BE DONE IN A ALL OR NOTHING FASHION
-- Stop point --    
PREPARE TRANSACTION 't1';
COMMIT PREPARED 't1' || ROLLBACK PREPARED 't1' (decision requires awareness and coordination)
Run Code Online (Sandbox Code Playgroud)

同时连接到 db2 将执行以下脚本:

BEGIN;
-- DO THINGS TO BE DONE IN A ALL OR NOTHING FASHION
-- Stop point --  
PREPARE TRANSACTION 't2';

COMMIT PREPARED 't2' || ROLLBACK PREPARED 't2'
Run Code Online (Sandbox Code Playgroud)
  • 协调器进程-- Stop point --(例如执行该语句的应用程序,或者 psql 客户端控制台或 pgAdminII 后面的人员)应停止执行两个脚本(实际上不执行任何进一步的指令,这就是我所说的停止)。

  • 然后,首先在 db1 上(然后在 db2 上,反之亦然)协调器进程(无论是否为人为进程)必须PREPARE TRANSACTION在每个连接上运行。

    • 如果其中之一失败,则协调器必须 ROLLBACK PREPARED 在事务已准备好的数据库和 ROLLBACK 其他数据库上运行。
    • 如果没有人失败,协调器必须在所有涉及的数据库上运行 COMMIT PREPARED,这是一项永远不会失败的操作(就像当你走出家门一步,所有东西都正确设置为安全退出时,你就已经离开家了)

Lau*_*lbe 15

我想你误会了PREPARE TRANSACTION

该语句结束事务的工作,也就是说,应该在所有工作完成后发出该语句。这个想法是,PREPARE TRANSACTION除了提交本身之外,执行提交期间可能失败的所有操作。那就是保证后续COMMIT PREPARED不会失败。

其思路是处理如下:

  • START TRANSACTION在分布式事务涉及的所有数据库上运行。

  • 做好所有的工作。如果有错误,ROLLBACK则所有交易。

  • PREPARE TRANSACTION在所有数据库上运行。如果在任何地方失败,请ROLLBACK PREPARED在已准备好事务的数据库和ROLLBACK其他数据库上运行。

  • 一旦PREPARE TRANSACTION在所有地方都成功,就可以COMMIT PREPARED在所有涉及的数据库上运行。

这样,您就可以保证多个数据库之间的“全有或全无”。

这里我没有提到的一个重要组件是分布式事务管理器。它是一个软件,可以永久记住上述算法当前处理的位置,以便在崩溃后可以进行清理或继续提交。

如果没有分布式事务管理器,两阶段提交没有多大价值,而且实际上很危险:如果事务陷入“准备”阶段但尚未提交,它们将继续持有锁并且(在以下情况下) PostgreSQL)即使在服务器重新启动时也会阻止自动清理工作,因为此类事务必须是持久的。

这很难做到正确。