天天看點

postgresql 分布式資料庫

1 分布式事務所用到的兩階段送出協定

兩階段送出的過程涉及到協調者和參與者。協調者可以看做成事務的發起者,同時也是事務的一個參與者。對于一個分布式事務來說,一個事務是涉及到多個參與者的。具體的兩階段送出的過程如下:

第一階段:

首先,協調者在自身節點的日志中寫入一條的日志記錄,然後所有參與者發送消息prepare T,詢問這些參與者(包括自身),是否能夠送出這個事務;

參與者在接受到這個prepare T 消息以後,會根據自身的情況,進行事務的預處理,如果參與者能夠送出該事務,則會将日志寫入磁盤,并傳回給協調者一個ready T資訊,同時自身進入預送出狀态狀态;如果不能送出該事務,則記錄日志,并傳回一個not commit T資訊給協調者,同時撤銷在自身上所做的資料庫改動;參與者能夠推遲發送響應的時間,但最終還是需要發送的。

第二階段:

協調者會收集所有參與者的意見,如果收到參與者發來的not commit T資訊,則辨別着該事務不能送出,協調者會将Abort T 記錄到日志中,并向所有參與者發送一個Abort T 資訊,讓所有參與者撤銷在自身上所有的預操作。

如果協調者收到所有參與者發來prepare T資訊,那麼協調者會将Commit T日志寫入磁盤,并向所有參與者發送一個Commit T資訊,送出該事務。若協調者遲遲未收到某個參與者發來的資訊,則認為該參與者發送了一個VOTE_ABORT資訊,進而取消該事務的執行。

參與者接收到協調者發來的Abort T資訊以後,參與者會終止送出,并将Abort T 記錄到日志中;如果參與者收到的是Commit T資訊,則會将事務進行送出,并寫入記錄一般情況下,兩階段送出機制都能較好的運作,當在事務進行過程中,有參與者當機時,他重新開機以後,可以通過詢問其他參與者或者協調者,進而知道這個事務到底送出了沒有。當然,這一切的前提都是各個參與者在進行每一步操作時,都會事先寫入日志。

唯一一個兩階段送出不能解決的困境是:當協調者在發出commit T消息後當機了,而唯一收到這條指令的一個參與者也當機了,這個時候這個事務就處于一個未知的狀态,沒有人知道這個事務到底是送出了還是未送出,進而需要資料庫管理者的介入,防止資料庫進入一個不一緻的狀态。當然,如果有一個前提是:所有節點或者網絡的異常最終都會恢複,那麼這個問題就不存在了,協調者和參與者最終會重新開機,其他節點最終也會收到commit T的資訊。

2使用兩階段送出注意事項(德哥一篇文章中的建議)

2.1. 不要使2PC時間過長,因為有2PC存在的話vacuum不能回收垃圾空間(這個我在之前的部落格也有寫到,哪怕是begin;開着不放都不行)。

2.2. 2PC時間過長還可能造成強制資料庫SHUTDOWN,如 transaction ID wraparound.

2.3. 2PC時間過長也可能帶來鎖時間過長的問題。

2.4. 是以沒必要的話建議不要開啟prepared transaction,由應用來實作2PC也是不錯的選擇。

3 分布式事務到資料檔案支援

Data/fxdb_twophase

3.1 prepare transaction

在 prepare transaction 的時候,在資料庫的目錄的 pg_twophase 檔案夾生成state file,檔案名為事務的XID.要生成state file的主要原因是,在這一過程中,已完成了資源的釋放,把不能釋放的記錄下來,以便2 commit時候釋放.

3.2 commit prepared

把state file讀出來解析,接着釋放資源,之後就是記錄日志,并把state file删除.

3.3 總結fxdb_twophase的作用

當在prepare transaction成功,之後系統挂掉,這時state file已建立成功,保留在硬碟上,當系統重新開機後,會根據日志和state file重構XA事物,在系統啟動完成後,可以接着 commit prepared 或 rollback prepared 這個事物。

postgresql中兩階段送出實作原理

TwoPhaseStateData

typedef struct TwoPhaseStateData

{

GlobalTransaction freeGXacts;

int numPrepXacts;

GlobalTransaction prepXacts[1];

} TwoPhaseStateData;

GlobalTransactionData

typedef struct GlobalTransactionData

{

PGPROC proc;

BackendId dummyBackendId;

TimestampTz prepared_at;

XLogRecPtr prepare_lsn;

Oid owner;

TransactionId locking_xid;

bool valid;

char gid[GIDSIZE];

#ifdef FOUNDER_XDB_SE

TransactionId xid;

#endif

}GlobalTransactionData;

TwoPhaseFileHeader

typedef struct TwoPhaseFileHeader

{

uint32 magic;

uint32 total_len;

TransactionId xid;

Oid database;

TimestampTz prepared_at;

Oid owner;

int32 nsubxacts;

int32 ncommitrels;

int32 nabortrels;

int32 ninvalmsgs;

bool initfileinval;

char gid[GIDSIZE];

} 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);

XLogFlush(recptr);

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.

繼續閱讀