|
PostgreSQL has its own transaction implementation model. Generally divided into three levels: top layer, middle layer and bottom layer.
1. Top Layer
Top Layer is primarily user-controlled and visible to the user. This layer of affairs, mainly by the user to decide the start and end of the transaction. The transaction lifecycle is user-controlled and is high-level.
Which is commonly referred to as the transaction block, transaction block. When the user initiates: BEGIN, COMMIT, ROLLBACK, SAVEPOINT, ROLLBACK TO or RELEASE command,
PG's traffic cop will re-forward these calls to the Top Layer routine, the corresponding method is as follows:
BeginTransactionBlock
EndTransactionBlock
UserAbortTransactionBlock
DefineSavepoint
RollbackToSavepoint
ReleaseSavepoint
2.Middle Layer
This layer is basically statement-level. Not visible to the user, that is the user can not control the specific life cycle. The processing of this layer corresponds to the statement. By postgres.c processing, the corresponding method is as follows:
StartTransactionCommand
CommitTransactionCommand
AbortCurrentTransaction
3.Bottom Layer
This is the lowest-level implementation of transactional atomicity, which is a row-level level. Is the real sense of the transaction to achieve, and nested transaction processing.
The corresponding method is as follows:
StartTransaction
CommitTransaction
AbortTransaction
CleanupTransaction
StartSubTransaction
CommitSubTransaction
AbortSubTransaction
CleanupSubTransaction
From the above three-tier model, we can see that PG transaction management granularity from coarse to fine. Call from the middle to the bottom of the call. In addition, if there are user-level transaction control, such as the "BEGIN", etc., were postgres.c forwarded to the top layer.
Consider the following scenario:
1) BEGIN
2) SELECT * FROM foo
3) INSERT INTO foo VALUES (...)
4) COMMIT
Then call the corresponding method calls are as follows:
/ StartTransactionCommand;
/ StartTransaction;
1) < ProcessUtility; < BEGIN
\ BeginTransactionBlock;
\ CommitTransactionCommand;
/ StartTransactionCommand;
2) / ProcessQuery; < < SELECT ...
\ CommitTransactionCommand;
\ CommandCounterIncrement;
/ StartTransactionCommand;
3) / ProcessQuery; < < INSERT ...
\ CommitTransactionCommand;
\ CommandCounterIncrement;
/ StartTransactionCommand;
/ ProcessUtility; < < COMMIT
4) < EndTransactionBlock;
\ CommitTransactionCommand;
\ CommitTransaction;
In the first step, the user-defined transaction boundary begins and is the starting point of the lifecycle of the transaction.
First of all, "BEGIN" is also used as a command statement, so this step is also required in the middle layer of the method call to be surrounded.
Then start a low-level transaction, that is, by the middle layer call bottom layer method. StartTransaction generates the vxid (virtual transaction id, which is the backend id and the local transaction id).
Determine whether the current transaction is read-only or is currently in a recovery state. Transaction isolation level, the transaction is asynchronous and other information is initialized here. Set the transState state to: TRANS_INPROCESS.
Because there "BEGIN" order, it is the top layer. So it is transferred to the BeginTransactionBlock logic. Start the transaction block processing and set the transaction state to TBLOCK_START.
From the transaction block and the transaction is the difference between the state of affairs set up not the same.
Start trasaction block is the transaction state is set to: TBLOCK_START, and the general start transaction is to set it to: TRANS_INPROCESS.
The transaction block is distinct from the transaction, in the state.
In the second step, the select query is initiated. As mentioned above, the middle layer is the corresponding with the command statement. So for the select query, but also with the middle layer in the method call to be surrounded.
Additional CIDs are added for MVCC visibility judgments in the same transaction.
In the third step, the insert operation is initiated. This step is largely logically equivalent to the second step. Only in dealing with insert, the need to update the current xid tupler header to go.
Usually related to insert, update, delete and other operations are closely related with the heap, heap related to the physical tuple add and delete. In this layer, the PG is required for each DML
The operation assigns a transaction ID and updates the transaction ID to xmin or xmax in the tuple header. In order to achieve MVCC. In addition, this layer will increase the commandID, mainly used to achieve the same transaction to determine the visibility.
In the fourth step, the user initiates a commit to commit the transaction. The end of the transaction lifecycle. This step is equivalent to the first step. Here not only need to end the top layer of the transaction block. Also need to end the low-level of the transaction, implementation
Transaction atomicity. Also need to release resources, release the buffer pin, release the lock and so on.
From the above explanation, we can see more clearly, PG in the general logic of the realization of the affairs. The full realization of ACID function.
Atomity: Atomicity is implemented at the low-level level, starting at StartTransaction and ending at CommitTransaction.
Consistency: Consistency is achieved at the statement level, ie, the middle layer. In addition tuple header updated xmin, xmax, cmin, cmax for the transaction to provide a basis for judging the visibility.
Isolation: At StartTransaction, initializes the transaction isolation level. Provides the basis for creating a snapshot for MVCC.
Duarability: CommitTransaction implementation, committing transaction logs, and so on. Providing the foundation for data recovery and persistence. To achieve WAL (Write Ahead Log) function. |
|
|
|