PostgreSQLDBA(23)-MVCC#3(事务快照和隔离级别)

如前所述,PostgreSQL使用一种称为快照隔离Snapshot Isolation (SI)实现并发控制,下面简单介绍PostgreSQL中与之相关的快照和隔离级别等基本概念。
注意:
1.简单起见,本节暂不讨论事务回滚的情况.
2.如无说明,默认的事务隔离级别为READ COMMITTED

10年积累的成都网站建设、网站设计经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站制作后付款的网站建设流程,更有深圳免费网站建设让你可以放心的选择与我们合作。

一、事务快照

事务快照(简称Snapshot)存储所有事务在某个时间点是否处于活动状态(正在进行或尚未启动)的相关信息.
通过txid_current_snapshot()函数可以获取当前事务的快照信息.


testdb=# select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2315:2315:
(1 row)

快照的格式如下为xmin : xmax : xip_list
其中:
xmin : 最早仍活跃的事务txid,早于此txid的事务要么被提交并可见,要么回滚并废弃。
xmax : ShmemVariableCache->latestCompletedXid + 1,即最后已完结事务(COMMITTED/ABORTED)的txid + 1 。在”拍摄”快照时,所有大于或等于此txid的事务尚未启动,因此不可见。
xip_list : 在”拍摄”快照时处于活动状态的事务txid。该列表包含xmin和xmax之间的活动txid。
总结一下,简单来说,对于给定的事务txid:
txid ∈ [2,xmin),是过去的事务,对此快照均可见;
txid ∈ [xmin,xmax),txid仍处于IN_PROGRESS状态,则不可见;COMMITED状态,则可见;ABORTED的事务,亦不可见;
txid ∈ [xmax,∞),是未来的事务,对此快照均不可见;

启动三个客户端(psql),执行begin启动事务:
session 1


11:36:04 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:36:09 (xdb@[local]:5432)testdb=#* select 1;
 ?column? 
----------
        1
(1 row)
11:36:09 (xdb@[local]:5432)testdb=#* select txid_current();
 txid_current 
--------------
         2327
(1 row)
11:36:09 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2327:
(1 row)

session 2


11:36:02 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:36:17 (xdb@[local]:5432)testdb=#* select 1;
 ?column? 
----------
        1
(1 row)
11:36:17 (xdb@[local]:5432)testdb=#* select txid_current();
 txid_current 
--------------
         2328
(1 row)
11:36:17 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2327:
(1 row)

session 3


11:35:59 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:36:18 (xdb@[local]:5432)testdb=#* select 1;
 ?column? 
----------
        1
(1 row)
11:36:18 (xdb@[local]:5432)testdb=#* select txid_current();
 txid_current 
--------------
         2329
(1 row)
11:36:18 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2327:
(1 row)

session 1/2/3的txid分别是2327/2328/2329,快照均为2327:2327:,即小于2327的事务可见,大于等于2327的不可见.

session 2创建数据表,插入数据,提交,重新开启事务,插入数据.


11:36:17 (xdb@[local]:5432)testdb=#* 
11:37:24 (xdb@[local]:5432)testdb=#* create table t_session2 (id int);
CREATE TABLE
11:37:28 (xdb@[local]:5432)testdb=#* insert into t_session2 values(1);
INSERT 0 1
11:37:28 (xdb@[local]:5432)testdb=#* commit;
COMMIT
11:37:28 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:38:25 (xdb@[local]:5432)testdb=#* insert into t_session2 values(2);
INSERT 0 1
11:38:31 (xdb@[local]:5432)testdb=#*

查看session 1/2/3的快照信息变化.
session 1


11:38:03 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2329:
(1 row)

session 1的快照信息变为2327:2329:,即小于2327的事务可见,大于等于2329的事务不可见.
session 2


11:39:02 (xdb@[local]:5432)testdb=#* select txid_current();
 txid_current 
--------------
         2330
(1 row)
11:39:14 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2329:2327
(1 row)

session 2的快照信息变为2327:2329:2327,即小于2327的事务可见,大于等于2329的事务不可见,2327事务进行中,亦不可见.

session 3


11:36:18 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2329:2327
(1 row)
11:39:57 (xdb@[local]:5432)testdb=#*

session 3的快照信息与session 2一致.

session 1创建数据表并插入数据


11:40:36 (xdb@[local]:5432)testdb=#* create table t_session1 (id int);
CREATE TABLE
11:45:10 (xdb@[local]:5432)testdb=#* insert into t_session1 values(1);
INSERT 0 1
11:45:19 (xdb@[local]:5432)testdb=#*

session 2查询数据表t_session1


11:39:17 (xdb@[local]:5432)testdb=#* select * from t_session1;
ERROR:  relation "t_session1" does not exist
LINE 1: select * from t_session1;
11:49:58 (xdb@[local]:5432)testdb=#! select txid_current_snapshot(); --> 一旦出错,就要回滚事务了
ERROR:  current transaction is aborted, commands ignored until end of transaction block
11:50:28 (xdb@[local]:5432)testdb=#! commit; --> 虽然是commit,按提示信息,其实是rollback.
ROLLBACK

报错,提示关系不存在.
注意:事务处理过程中,如出错,Oracle仍可提交先前成功执行的操作,但PG要求回滚事务

在上述案例中,在同一个事务中,执行新的SQL时事务快照是会变化的,这是因为默认的隔离级别READ COMMITTED的机制使然,如隔离级别为REPEATABLE READ则事务快照只会获取一次并记录获取快照时事务的状态.

二、隔离级别

在PostgreSQL中,有四种隔离级别,分别是READ UNCOMMITTED/READ COMMITTED/REPEATABLE READ/SERIALIZABLE.
其中READ UNCOMMITTED与READ COMMITTED一致,没有区别;SERIALIZABLE级别较少使用,这里不作叙述.
READ COMMITTED
沿用第1小节的session 1/2.
session 2重新启动事务


12:06:17 (xdb@[local]:5432)testdb=#*  select txid_current();
 txid_current 
--------------
         2331
(1 row)
12:06:23 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2327:2331:2327,2329
(1 row)

session 1提交事务


11:40:36 (xdb@[local]:5432)testdb=#* create table t_session1 (id int);
CREATE TABLE
11:45:10 (xdb@[local]:5432)testdb=#* insert into t_session1 values(1);
INSERT 0 1
11:45:19 (xdb@[local]:5432)testdb=#* commit;
COMMIT
12:07:21 (xdb@[local]:5432)testdb=#

这时候session 2可读取session 1中的t_session1数据表


12:08:21 (xdb@[local]:5432)testdb=#* select * from t_session1;
 id 
----
  1
(1 row)

REPEATABLE READ
session 1,隔离级别为READ COMMITTED


12:12:14 (xdb@[local]:5432)testdb=# START TRANSACTION ISOLATION LEVEL READ COMMITTED; 
START TRANSACTION

session 2,隔离级别为REPEATABLE READ


12:12:41 (xdb@[local]:5432)testdb=# START TRANSACTION ISOLATION LEVEL REPEATABLE READ; 
START TRANSACTION
12:12:43 (xdb@[local]:5432)testdb=#* select * from t_session1;
 id 
----
  1
(1 row)

仍使用上述场景,session 1插入数据,提交事务,这时候新插入的数据对session 2仍不可见.
session 1


12:12:18 (xdb@[local]:5432)testdb=#* insert into t_session1 values(2);
INSERT 0 1
12:13:22 (xdb@[local]:5432)testdb=#* commit;
COMMIT
12:13:25 (xdb@[local]:5432)testdb=#

session 2


12:13:38 (xdb@[local]:5432)testdb=#* select * from t_session1;
 id 
----
  1 --> 新插入的数据2不可见
(1 row)
12:13:58 (xdb@[local]:5432)testdb=#* select txid_current_snapshot();
 txid_current_snapshot 
-----------------------
 2329:2332:2329
(1 row)

session 2的事务快照只会获取一次.
获取事务快照的逻辑以及如何确定heap tuple是否可见的逻辑后续在源码解读中将作具体解析.

三、参考资料

Concurrency Control

四、号外

打错字,PG也要求回滚事务?


12:06:25 (xdb@[local]:5432)testdb=#* selet * from t_session1;
ERROR:  syntax error at or near "selet"
LINE 1: selet * from t_session1;
        ^
12:07:56 (xdb@[local]:5432)testdb=#! select * from t_session1;
ERROR:  current transaction is aborted, commands ignored until end of transaction block
12:08:02 (xdb@[local]:5432)testdb=#! commit;
ROLLBACK


文章题目:PostgreSQLDBA(23)-MVCC#3(事务快照和隔离级别)
标题路径:http://myzitong.com/article/peehic.html