postgresql中两阶段提交实现原理
TwoPhaseStateData
/*
* Two Phase Commit shared state. Access to this struct is protected
* by TwoPhaseStateLock.
*/
typedef struct TwoPhaseStateData
{
/* Head of linked list of free GlobalTransactionData structs */
GlobalTransaction freeGXacts;
/* Number of valid prepXacts entries. */
intnumPrepXacts;
/*
* There are max_prepared_xacts items in this array, but C wants a
* fixed-size array.
*/
GlobalTransaction prepXacts[1];/* VARIABLE LENGTH ARRAY */
} TwoPhaseStateData;/* VARIABLE LENGTH STRUCT */
GlobalTransactionData
typedef struct GlobalTransactionData
{
PGPROCproc;/* dummy proc */
BackendIddummyBackendId; /* similar to backend id for backends */
TimestampTz prepared_at;/* time of preparation */
XLogRecPtrprepare_lsn;/* XLOG offset of prepare record */
Oidowner;/* ID of user that executed the xact */
TransactionId locking_xid;/* top-level XID of backend working on xact */
boolvalid;/* TRUE if fully prepared */
chargid[GIDSIZE];/* The GID assigned to the prepared xact */
#ifdef FOUNDER_XDB_SE
TransactionId xid;
#endif
}GlobalTransactionData;
TwoPhaseFileHeader
typedef struct TwoPhaseFileHeader
{
uint32magic;/* format identifier */
uint32total_len;/* actual file length */
TransactionId xid;/* original transaction XID */
Oiddatabase;/* OID of database it was in */
TimestampTz prepared_at;/* time of preparation */
Oidowner;/* user running the transaction */
int32nsubxacts;/* number of following subxact XIDs */
int32ncommitrels;/* number of delete-on-commit rels */
int32nabortrels;/* number of delete-on-abort rels */
int32ninvalmsgs;/* number of cache invalidation messages */
boolinitfileinval;/* does relcache init file need invalidation? */
chargid[GIDSIZE];/* GID for transaction */
} TwoPhaseFileHeader;
Variable
static THREAD_LOCAL TwoPhaseStateData *TwoPhaseState;
4 分布式事务创建
4.1 Where the transcation id is come from?
Each global transaction is associated with a global transaction
identifier (GID). The client assigns a GID to a postgres transaction with the PREPARE TRANSACTION command.
4.2 Where the transaction is stored in server?
We keep all active global transactions in a shared memory array.When the PREPARE TRANSACTION command is issued, the GID is reserved for the transaction in the array. This is done before a WAL entry is made, because the reservation checks for duplicate GIDs and aborts the transaction if there already is a global transaction in prepared state with the same GID.
4.3 global transaction has a dummy PGPROC
A global transaction (gxact) also has a dummy PGPROC that is entered
into the ProcArray array; this is what keeps the XID considered
running by TransactionIdIsInProgress. It is also convenient as a
PGPROC to hook the gxact's locks to.
5 分布式事务Commit
recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT_PREPARED, rdata);
/* Always flush, since we're about to remove the 2PC state file */
XLogFlush(recptr);
/*
* Mark the transaction aborted in clog. This is not absolutely necessary
* but we may as well do it while we are here.
*/
TransactionIdAbortTree(xid, nchildren, children);
5分布式事务Recovery
In order to survive crashes and shutdowns, all prepared transactions must be stored in permanent storage. This includes locking information, pending notifications etc. All that state information is written to the per-transaction state file in the pg_twophase directory.
5.1RecoverPreparedTransactions
In order to survive crashes and shutdowns, all prepared transactions must be stored in permanent storage. This includes locking information, pending notifications etc. All that state information is written to the per-transaction state file in the pg_twophase directory.
5.2RecoverPreparedTransactions
Scan the pg_twophase directory and reload shared-memory state for each
prepared transaction
5.3lock_twophase_standby_recover
Re-acquire a lock belonging to a transaction that was prepared, when starting up into hot standby mode.