【问题标题】:Using two phase commits on postgres在 postgres 上使用两阶段提交
【发布时间】:2020-03-22 06:24:35
【问题描述】:

假设在“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'

我猜是连接到 db2

BEGIN;
PREPARE TRANSACTION 'pepe'; -- this fails (the name of the transacttion, 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'

正如您所见,我不知道如何在 postgres 上使用两阶段提交。

TL;DR

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

我已阅读 at oficial postgres documentation,为了在两个或多个不相关的 postgres 数据库中同步工作,我们可以使用所谓的“两阶段提交”协议的实现。

所以我开始尝试看看人们是如何在 postgres 中实际使用它们的,我没有看到任何实际的例子,最多我得到一个人的this post,他试图尝试连接到几个 postgres 客户端不同的数据库,以模拟并行运行的多个进程对多个数据库的处理,这些数据库应该以感激(全部提交)或可怕的方式(全部回滚)结束。

我看到的其他来源是:

我真的很困惑,我希望 horse_with_no_name 出现在这里并启发我(就像过去发生的那样)或任何其他可以帮助我的慈善灵魂。

提前致谢!

分辨率(在 Laurenz 的回答之后)

连接到 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)

同时连接到 db2 这些将是要执行的脚本:

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

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

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

    • 如果其中一个失败,则协调器必须在已经准备好事务的那些数据库上运行 ROLLBACK PREPARED ,在其他数据库上运行 ROLLBACK
    • 如果没有人失败,则协调员必须在所有相关数据库上运行 COMMIT PREPARED,这是一项永远不会失败的操作(例如,当您离家一步,所有东西都已正确设置为安全退出时,就已经有了家)李>

【问题讨论】:

  • 猜这对我有帮助...stackoverflow.com/questions/8953423/…,我花了很多时间查看两阶段提交而不是准备事务...:S
  • 上面的链接有一个奇怪的例子,我猜,根据 Laurenz 的回答,它没有准备两个交易,它使用两个数据库...
  • 请注意,您提到的一篇文章已移至此处:endpointdev.com/blog/2010/07/…

标签: postgresql two-phase-commit


【解决方案1】:

我想你误解了PREPARE TRANSACTION

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

思路是处理如下:

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

  • 完成所有工作。如果有错误,ROLLBACK所有交易。

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

  • 一旦PREPARE TRANSACTION 在所有地方都成功了,就在所有相关数据库上运行COMMIT PREPARED

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

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

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

这很难做到。

【讨论】:

  • “我认为你误解了 PREPARE TRANSACTION”
  • 如果你没有事务管理器,最好不要使用分布式事务。
  • 好的,我明白了,我想我用来连接数据库的应用程序可能是所谓的事务管理器的监督进程,不是吗?但是,再一次..让我们在这里小步前进!我们有一个用 Golang 编写的应用程序,我们使用该应用程序对数据库进行处理。
  • 当然可以。但是它最好确保保留这些信息,以便它可以在崩溃后恢复工作。否则你最终会“卡住”准备好的交易。
  • 是的,看起来你做对了。请注意“如何编写分布式事务管理器?”是一个过于宽泛的问题。尝试获取有关配音的文献。
猜你喜欢
  • 2011-11-15
  • 2011-11-24
  • 2013-05-21
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多