您现在的位置是:首页 >技术教程 >pg事务:2PC网站首页技术教程
pg事务:2PC
什么是2PC事务?
事务原子性要求事务必须整体完成或者回滚。在多个联接的数据库等情况下的分布式事务中,必须为事务提供一致性状态,以满足分布式事务的原子性。与其他数据库一样,pg库也提供了 two-phase commit protocol(2PC)两阶段提交协议。
分布式事务实现方案很多,2PC是其中最基础也是最常见的。分布式事务包括原子提交、原子可见性、全局一致性,2PC只是原子提交的实现方案。
prepare transaction
FDW可以自己处理2PC事务,pg也提供了显示使用2PC事务的方法prepare transaction。prepare transaction发起后,就不会与会话有任何关联,它状态会被保存下来。prepare transaction并不是设计为在应用或者交互式会话中使用,除非你在编写一个事务管理器,所以推荐(默认)关闭的。
语法:
PREPARE TRANSACTION transaction_id
COMMIT PREPARED transaction_id
ROLLBACK PREPARED transaction_id
注意:
- 这的transaction_id不是内部事务id,只是一个声明的字符串
- PREPARE TRANSACTION 必须在事务块中,事务块以BEGIN|START STRANSATION开始
- max_prepared_transactions控制prepare事务数,默认为0关闭,需要打开才能使用prepare事务
开启一个prepare事务
lzldb=# begin;
BEGIN
lzldb=*# PREPARE TRANSACTION 'lzl';
PREPARE TRANSACTION
lzldb=# select * from pg_prepared_xacts ;
transaction | gid | prepared | owner | database
-------------+-----+-------------------------------+-------+----------
719 | lzl | 2023-04-29 16:08:45.866022+08 | pg | lzldb
(1 row)
lzldb=# rollback prepared 'lzl';
ROLLBACK PREPARED
lzldb=# select * from pg_prepared_xacts ;
transaction | gid | prepared | owner | database
-------------+-----+----------+-------+----------
(0 rows)
pg_twophase目录
前面说过,prepare事务与会话无关,当开启一个prepare事务后,事务状态信息保存在缓存中。
为了保证事务不丢失,prepare事务也会落盘,到pg_twophase
目录。
其实并不是关库才会导致prepare事务落盘,而是checkpoint
源码src/backend/access/transam/twophase.c
void
CheckPointTwoPhase(XLogRecPtr redo_horizon)
{
...
TRACE_POSTGRESQL_TWOPHASE_CHECKPOINT_START(); //checkpoint开始
...
fsync_fname(TWOPHASE_DIR, true); //调用fsync落盘
TRACE_POSTGRESQL_TWOPHASE_CHECKPOINT_DONE();//checkpoint完成
...
}
于是尝试开启一个prepare事务并做checkpoint
[pg@lzl pg_twophase]$ ll
total 0
lzldb=*# PREPARE TRANSACTION 'lzl';
PREPARE TRANSACTION
lzldb=# checkpoint;
CHECKPOINT
[pg@lzl pg_twophase]$ ll
total 4
-rw------- 1 pg pg 116 Apr 29 16:33 000002D0
orphaned prepared transactions
如果一个prepared事务没有完成(prepared事务不提交或回滚),而prepared事务又与会话无关,如果不显示结束这个事务的话,prepared事务仍然存在(会话断开后一般事务会回退),这就是orphaned prepared transactions。
orphaned prepared transactions会一直持有一些锁、元组的资源,导致vacuum无法回收和清理死元组,甚至阻止事务ID回卷。比如一个prepared事务忘记提交或者回滚了,如果没有外部事务管理机制来监控,这个parepared事务将可能不被发现并永远存在,最终导致严重的问题。所以建议max_prepared_transactions=0
(默认)或者通过pg_prepared_xacts
视图监控prepared事务
下面模拟一个孤儿prepared事务无限期阻塞的情况
--开启一个prepared事务并断开会话
lzldb=# begin;
BEGIN
lzldb=*# insert into lzl1 values(1);
INSERT 0 1
lzldb=*# PREPARE TRANSACTION 'lzl';
PREPARE TRANSACTION
lzldb=# q
--会话断开prepared事务仍然存在
postgres=# select * from pg_prepared_xacts ;
transaction | gid | prepared | owner | database
-------------+-----+-------------------------------+-------+----------
721 | lzl | 2023-04-29 17:08:59.597678+08 | pg | lzldb
--ddl 阻塞
lzldb=# alter table lzl1 add column b int;
--查看锁的情况
lzldb=# select locktype,relation,pid,mode from pg_locks where relation=32808;
locktype | relation | pid | mode
----------+----------+-------+---------------------
relation | 32808 | 26136 | AccessExclusiveLock
relation | 32808 | | RowExclusiveLock
--结束prepared事务,ddl执行完成
lzldb=# rollback prepared 'lzl';
ROLLBACK PREPARED
lzldb=# alter table lzl1 add column b int;
ALTER TABLE
2PC事务参考
http://postgres.cn/docs/13/sql-prepare-transaction.html
https://www.highgo.ca/2020/01/28/understanding-prepared-transactions-and-handling-the-orphans/
https://wiki.postgresql.org/wiki/Atomic_Commit_of_Distributed_Transactions