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

猜你喜欢

转载自blog.csdn.net/qq_40687433/article/details/130783427
2PC