PostgreSQL transaction

1. ACID characteristics of transactions

  In day-to-day operations, for a group of related operations, it is usually required that all succeed or all fail. In a relational database, this set of related operations is called a transaction. The four properties that transactions have are referred to as ACID.

  • Atomicity : It is guaranteed that the operations in the transaction either all succeed or all fail, and not only part of them succeed.
  • Consistency : The validity of data modification and compliance with certain business rules.
  • Isolation : Determines the degree of visibility and mutual influence between concurrent transactions.
  • Durability : Ensure that committed transactions must take effect permanently.

Second, the use of business

2.1 Automatically commit transactions

postgres=# \echo :AUTOCOMMIT
on
postgres=# create table accounts(id serial primary key,user_name varchar(50),balance numeric(10,4));
CREATE TABLE
postgres=# alter table accounts add constraint bal_check check(balance >= 0);
ALTER TABLE
postgres=# insert into accounts(user_name, balance) values ('usera', 6000);
INSERT 0 1
postgres=# table accounts;
 id | user_name |  balance
----+-----------+-----------
  1 | usera     | 6000.0000
(1 row)

2.2 Manually submit transactions

postgres=# \set AUTOCOMMIT off
postgres=# \echo :AUTOCOMMIT
off
postgres=# begin;
BEGIN
postgres=*# insert into accounts(user_name, balance) values ('userb', 0);
INSERT 0 1
postgres=*# table accounts;
 id | user_name |  balance
----+-----------+-----------
  1 | usera     | 6000.0000
  2 | userb     |    0.0000
(2 rows)

postgres=*# commit;
COMMIT
postgres=# table accounts;
 id | user_name |  balance
----+-----------+-----------
  1 | usera     | 6000.0000
  2 | userb     |    0.0000
(2 rows)

2.3 Manual rollback transaction

postgres=# begin;
BEGIN
postgres=*# insert into accounts(user_name, balance) values ('userc', 2000);
INSERT 0 1
postgres=*# table accounts;
 id | user_name |  balance
----+-----------+-----------
  1 | usera     | 6000.0000
  2 | userb     |    0.0000
  3 | userc     | 2000.0000
(3 rows)

postgres=*# rollback;
ROLLBACK
postgres=# table accounts;
 id | user_name |  balance
----+-----------+-----------
  1 | usera     | 6000.0000
  2 | userb     |    0.0000
(2 rows)

2.4 Manually roll back the transaction to the savepoint

postgres=# begin;
BEGIN
postgres=*# insert into accounts(user_name, balance) values ('userc', 2000);
INSERT 0 1
postgres=*# savepoint sv1;
SAVEPOINT
postgres=*# insert into accounts(user_name, balance) values ('userd', 0);
INSERT 0 1
postgres=*# table accounts;
 id | user_name |  balance
----+-----------+-----------
  1 | usera     | 6000.0000
  2 | userb     |    0.0000
  4 | userc     | 2000.0000
  5 | userd     |    0.0000
(4 rows)

postgres=*# rollback to sv1;
ROLLBACK
postgres=*# commit;
COMMIT
postgres=# table accounts;
 id | user_name |  balance
----+-----------+-----------
  1 | usera     | 6000.0000
  2 | userb     |    0.0000
  4 | userc     | 2000.0000
(3 rows)

3. Concurrency and isolation

When multiple users access the same data, it may cause the following problems:

  • Dirty read : A transaction can read uncommitted modifications of other transactions.
  • Nonrepeatable read (nonrepeatable read) : After a transaction reads a record, the data changes when the record is read again (modified and submitted by other transactions).
  • Phantom read : After a transaction queries some data according to a certain condition, the number of results changes when the same query is executed again (another transaction adds or deletes some data and completes the commit). Phantom reads are somewhat similar to non-repeatable reads, both of which are result changes caused by other transactions modifying data.
  • Lost update : When two transactions read a certain record at the same time, and then modify and commit them separately, the modification of the transaction submitted first will be lost.

To address concurrency issues, the SQL standard defines 4 different transaction isolation levels (low to high):

  • Read Uncommitted (read uncommitted) : The lowest isolation level, in fact, is not isolated, and any transaction can see the uncommitted modifications of other transactions; this level may generate various concurrency exceptions. However, PostgreSQL eliminates dirty reads at the Read Uncommitted level because its implementation is equivalent to Read Committed.
  • Read Committed (Read Committed) : A transaction can only see data that has been committed by other transactions, which solves the problem of dirty reading, but there are problems of non-repeatable reading, phantom reading, and lost updates. This is the default isolation level for PostgreSQL.
  • Repeated Read : A transaction reads the same data unchanged, even if other transactions modify and submit the data; but if other transactions delete the record, the data can no longer be queried ( phantom read). Repeatable reads in the SQL standard may have phantom reads, but PostgreSQL eliminates phantom reads at the repeatable read level.
  • Serializable (serializable) : The highest isolation level, transactions are executed serially, without concurrency.
isolation level dirty read non-repeatable read Phantom reading update lost
Read Uncommitted Possibly, but PostgreSQL does not possible possible possible
Read Committed impossible possible possible possible
Repeatable Read impossible impossible Possibly, but PostgreSQL does not impossible
Serializable impossible impossible impossible impossible

Example of modifying the isolation level :

--显示数据库隔离级别
show transaction_isolation;

--事务中修改隔离级别
begin;
set transaction isolation level {
   
   serializable|repeatable read|read committed|read uncommitted};
……
……
commit;

Four, two-phase commit

  The PostgreSQL database supports the two-phase commit protocol (this feature is disabled by default and is only used in distributed architectures). In a distributed system, transactions often include operations on multiple databases. Although operations on a single database can guarantee atomicity, the atomicity between multiple databases needs to be achieved through two-phase commit. Committing is the key to implementing distributed transactions.

  The two-phase commit protocol has the following 5 steps :

  • The application first calls each database to do some operations, but does not commit the transaction. The application then calls the commit method in the transaction coordinator (which may also be implemented by the application itself).
  • The transaction coordinator will contact each database involved in the transaction and notify them that they are ready to commit the transaction. This is the beginning of the first phase, when the PREPARE TRANSACTION command is called in PostgreSQL.
  • After each database receives the PREPARE TRANSACTION command, PostgreSQL will write the ready-to-commit information into the persistent storage area, and if it cannot complete this, it will directly return failure to the transaction coordinator.
  • The transaction coordinator receives responses from all databases.
  • In the second phase, if any database fails in the first phase, the transaction coordinator will send a rollback command "ROLLBACK PREPARED" to each database. If the responses of all databases are successful, send COMMIT PREPARED command to each database to notify each database that the transaction is successful.

Example :

--修改 max_prepared_transactions 参数,重启数据库生效
show max_prepared_transactions;
alter system set max_prepared_transactions = 10;
pg_ctl restart
show max_prepared_transactions;

--创建测试表
create table testtab01(id int primary key);

--开启事务,插入数据并进行第一阶段提交
postgres=# begin;
BEGIN
postgres=*# insert into testtab01 values(1);
INSERT 0 1
postgres=*# PREPARE TRANSACTION 'osdba_global_trans_0001';
PREPARE TRANSACTION
postgres=# table testtab01;
 id
----
(0 rows)

--重启数据库后进行第二阶段提交
[postgres@localhost ~]$ pg_ctl restart
waiting for server to shut down.... done
server stopped
waiting for server to start....2023-07-24 17:01:33.206 CST [10882] LOG:  00000: redirecting log output to logging collector process
2023-07-24 17:01:33.206 CST [10882] HINT:  Future log output will appear in directory "logs".
2023-07-24 17:01:33.206 CST [10882] LOCATION:  SysLogger_Start, syslogger.c:674
 done
server started
[postgres@localhost ~]$ psql
psql (13.6)
Type "help" for help.

postgres=# table testtab01;
 id
----
(0 rows)

postgres=# COMMIT PREPARED 'osdba_global_trans_0001';
COMMIT PREPARED
postgres=# table testtab01;
 id
----
  1
(1 row)

Guess you like

Origin blog.csdn.net/songyundong1993/article/details/131940967