Beginning PostgreSQL Transactions
Transactions can be a rough concept for those new to databases. Those new to PostgreSQL will find the implementation much different than those in other databases. The default isolation level in PostgreSQL is READ COMMITTED so that you only see data that has been committed. If someone else works on the same table, you will not see those changes until they are committed.
Start with an elementary table:
CREATE TABLE
demo=# insert into ex03 (a) values (1);
INSERT 0 1
Behind the scenes, the system sets up some information to track your data, as we learned here and here.
demo=# select a, ctid, xmin, xmax, cmin, cmax from ex03;
a | ctid | xmin | xmax | cmin | cmax
---+-------+------+------+------+------
1 | (0,1) | 768 | 0 | 0 | 0
(1 row)
The CTID was introduced last time, and the other columns may be new. The documentation details their definitions. For this blog, we are going to focus on XMIN and XMAX. XMIN is a 32-bit identifier that records the transaction identification that inserted the data in the column. Each successful update makes a new row with the updated columns. XMAX has two possible values. It will be zero when there is no transaction identification of a process working on that column. If there is a value for XMAX, then some activity is being done on that column. Clear as mud? It will be clearer in a few paragraphs.
First Transaction
START TRANSACTION
demo=*# update ex03 set a=2;
UPDATE 1
demo=*# select a, ctid, xmin, xmax, cmin, cmax from ex03;
a | ctid | xmin | xmax | cmin | cmax
---+-------+------+------+------+------
2 | (0,2) | 769 | 0 | 0 | 0
(1 row)
Second Transaction In Parallel
In another session, someone gets the data from our demo table.
a
---
1
(1 row)
Someone decides to add two rows of data to this. Following good practices, they start a transaction and then insert two rows of data.
START TRANSACTION
demo=*# insert into ex03 (a) values (100),(101);
INSERT 0 2
demo=*# select a, ctid, xmin, xmax, cmin, cmax from ex03;
a | ctid | xmin | xmax | cmin | cmax
-----+-------+------+------+------+------
1 | (0,1) | 768 | 769 | 0 | 0
100 | (0,3) | 770 | 0 | 0 | 0
101 | (0,4) | 770 | 0 | 0 | 0
(3 rows)
demo=*#
Previously, back in Terminal 1, the first row had an XMIN of 769 and no value for XMAX. Now, on parallel universe Terminal 2, the XMIN for the current session is 770. The two new rows just entered from Terminal 2 have the XMIN of this transaction. The first row where a = 1 having a great than zero XMAX warns us that someone else is changing that row.
You can get the current transaction ID by using SELECT txid_current();
Back To The First Transaction
Back in Terminal 1, there is no knowledge of what is going on in Terminal 2. We do not see the two new rows even if we check the table.
a | ctid | xmin | xmax | cmin | cmax
---+-------+------+------+------+------
2 | (0,2) | 769 | 0 | 0 | 0
(1 row)
demo=*# commit;
COMMIT
demo=# select a, ctid, xmin, xmax, cmin, cmax from ex03;
a | ctid | xmin | xmax | cmin | cmax
---+-------+------+------+------+------
2 | (0,2) | 769 | 0 | 0 | 0
(1 row)
demo=#
Back To The Second Transaction
Terminal 2 has no clue about what has been happening in Terminal 1. Users who check the table see what they saw before, except that the XMIN has now been set to 769 (the transaction ID of the last modification to the row from good old Terminal 1). XMAX has been set to zero, which informs us it is not currently under revision by someone or there was a roll back (later in this blog you will see this).
The user on Terminal 2 now issues a SAVEPOINT. This can be thought of as a transaction within a transaction. This user then modifies the row where a = 101 to a new value.
a | ctid | xmin | xmax | cmin | cmax
-----+-------+------+------+------+------
2 | (0,2) | 769 | 0 | 0 | 0
100 | (0,3) | 770 | 0 | 0 | 0
101 | (0,4) | 770 | 0 | 0 | 0
(3 rows)
demo=*# savepoint foo;
SAVEPOINT
demo=*# update ex03 set a = 103 where a = 101;
UPDATE 1
demo=*# select a, ctid, xmin, xmax, cmin, cmax from ex03;
a | ctid | xmin | xmax | cmin | cmax
-----+-------+------+------+------+------
2 | (0,2) | 769 | 0 | 0 | 0
100 | (0,3) | 770 | 0 | 0 | 0
103 | (0,5) | 771 | 0 | 1 | 1
(3 rows)
Note that CITD for value a = 101 is (0,4) before the update and then changes to (0,5) after. You can not see (0,4) right now, but it is there. Put that fact aside in your brain for a moment.
For some reason, this change is not liked. At this point, we can roll back to the SAVEPOINT or back out of the entire transaction, erasing all the changes made.
ROLLBACK
demo=*# select a, ctid, xmin, xmax, cmin, cmax from ex03;
a | ctid | xmin | xmax | cmin | cmax
-----+-------+------+------+------+------
2 | (0,2) | 769 | 0 | 0 | 0
100 | (0,3) | 770 | 0 | 0 | 0
101 | (0,4) | 770 | 771 | 0 | 0
(3 rows)
Terminal 2's user is now happy with the changes and commits them. CTID (0,5) has been replaced with CTID(0,4) logically.
COMMIT
demo=# select a, ctid, xmin, xmax, cmin, cmax from ex03;
a | ctid | xmin | xmax | cmin | cmax
-----+-------+------+------+------+------
2 | (0,2) | 769 | 0 | 0 | 0
100 | (0,3) | 770 | 0 | 0 | 0
101 | (0,4) | 770 | 771 | 0 | 0
(3 rows)
demo=#
Back Again To Terminal 1
The user on Terminal 1 now has the same view of the data that the user on Terminal 2 does.
Comments
Post a Comment