在 postgres 上使用两阶段提交
Using two phase commits on postgres
假设在“db1”中有一个名为“t1”的table,在“db2”中有另一个名为“t2”的table,我需要在两者上插入一条记录tables 或失败。
连接到 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 进行试验的人客户端连接到不同的数据库,以模拟多个进程 运行 宁在 pararell 做一些事情应该以感激的方式结束(所有提交)或可怕的方式(所有回滚)。
我看到的其他来源是:
- https://en.wikipedia.org/wiki/Two-phase_commit_protocol(此来源
很好地解释协议,但真的让我想知道在哪里或谁
我的“协调员”以及如何向“参与者”发送消息......我
只得到
prepare transaction <id>
、commit prepared <id>
或
rollback prepared <id>
个命令任我支配)
- Two phase commit
- https://dba.stackexchange.com/questions/145656/dependent-transaction-in-separate-database-connections
- https://www.endpointdev.com/blog/2010/07/distributed-transactions-and-two-phase/
- https://www.citusdata.com/blog/2017/11/22/how-citus-executes-distributed-transactions/
- (来自 golang 客户端应用程序)https://github.com/go-pg/pg/issues/490
拜托,我真的很困惑,我希望horse_with_no_name出现在这里并启发我(就像过去发生的那样)或任何其他可以帮助我的慈善灵魂。
提前致谢!
决议(在劳伦斯的回答之后)
连接到 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
。
- 如果 then 之一失败,则协调器必须 运行
ROLLBACK PREPARED
在那些已经准备好事务的数据库上,ROLLBACK
在其他数据库上。
- 如果没有人失败,协调器必须 运行 在所有涉及的数据库上提交准备,一项永远不会失败的操作(就像当你走出家门一步并且所有东西都正确设置时存在家安全退出)
我想你误会了PREPARE TRANSACTION
。
该语句结束事务处理,也就是说,它应该在所有工作完成后发出。这个想法是 PREPARE TRANSACTION
会做所有可能在提交过程中失败的事情,除了提交本身。也就是保证后面的COMMIT PREPARED
不会失败
思路是处理如下:
运行 START TRANSACTION
在分布式事务涉及的所有数据库上。
完成所有工作。如果有错误,ROLLBACK
所有交易。
运行 PREPARE TRANSACTION
在所有数据库上。如果在任何地方都失败了,运行 ROLLBACK PREPARED
在那些已经准备好事务的数据库上,ROLLBACK
在其他数据库上。
一旦 PREPARE TRANSACTION
在所有地方都成功了,运行 COMMIT PREPARED
在所有涉及的数据库上。
这样,您就可以保证跨多个数据库“全有或全无”。
这里我没有提到的一个重要组件是分布式事务管理器。它是一个软件,可以持久地记住上述算法处理当前的位置,以便它可以在崩溃后清理或继续提交。
如果没有分布式事务管理器,两阶段提交没有多大价值,而且实际上很危险:如果事务卡在“准备”阶段但尚未提交,它们将继续持有锁并(对于 PostgreSQL)阻止 autovacuum 工作 即使服务器重新启动 ,因为这样的事务必须是持久的。
这很难做到正确。
假设在“db1”中有一个名为“t1”的table,在“db2”中有另一个名为“t2”的table,我需要在两者上插入一条记录tables 或失败。
连接到 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 进行试验的人客户端连接到不同的数据库,以模拟多个进程 运行 宁在 pararell 做一些事情应该以感激的方式结束(所有提交)或可怕的方式(所有回滚)。
我看到的其他来源是:
- https://en.wikipedia.org/wiki/Two-phase_commit_protocol(此来源
很好地解释协议,但真的让我想知道在哪里或谁
我的“协调员”以及如何向“参与者”发送消息......我
只得到
prepare transaction <id>
、commit prepared <id>
或rollback prepared <id>
个命令任我支配) - Two phase commit
- https://dba.stackexchange.com/questions/145656/dependent-transaction-in-separate-database-connections
- https://www.endpointdev.com/blog/2010/07/distributed-transactions-and-two-phase/
- https://www.citusdata.com/blog/2017/11/22/how-citus-executes-distributed-transactions/
- (来自 golang 客户端应用程序)https://github.com/go-pg/pg/issues/490
拜托,我真的很困惑,我希望horse_with_no_name出现在这里并启发我(就像过去发生的那样)或任何其他可以帮助我的慈善灵魂。
提前致谢!
决议(在劳伦斯的回答之后)
连接到 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
。- 如果 then 之一失败,则协调器必须 运行
ROLLBACK PREPARED
在那些已经准备好事务的数据库上,ROLLBACK
在其他数据库上。 - 如果没有人失败,协调器必须 运行 在所有涉及的数据库上提交准备,一项永远不会失败的操作(就像当你走出家门一步并且所有东西都正确设置时存在家安全退出)
- 如果 then 之一失败,则协调器必须 运行
我想你误会了PREPARE TRANSACTION
。
该语句结束事务处理,也就是说,它应该在所有工作完成后发出。这个想法是 PREPARE TRANSACTION
会做所有可能在提交过程中失败的事情,除了提交本身。也就是保证后面的COMMIT PREPARED
不会失败
思路是处理如下:
运行
START TRANSACTION
在分布式事务涉及的所有数据库上。完成所有工作。如果有错误,
ROLLBACK
所有交易。运行
PREPARE TRANSACTION
在所有数据库上。如果在任何地方都失败了,运行ROLLBACK PREPARED
在那些已经准备好事务的数据库上,ROLLBACK
在其他数据库上。一旦
PREPARE TRANSACTION
在所有地方都成功了,运行COMMIT PREPARED
在所有涉及的数据库上。
这样,您就可以保证跨多个数据库“全有或全无”。
这里我没有提到的一个重要组件是分布式事务管理器。它是一个软件,可以持久地记住上述算法处理当前的位置,以便它可以在崩溃后清理或继续提交。
如果没有分布式事务管理器,两阶段提交没有多大价值,而且实际上很危险:如果事务卡在“准备”阶段但尚未提交,它们将继续持有锁并(对于 PostgreSQL)阻止 autovacuum 工作 即使服务器重新启动 ,因为这样的事务必须是持久的。
这很难做到正确。