温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2020-08-10 08:47:25 来源:ITPUB博客 阅读:163 作者:husthxd 栏目:关系型数据库

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

一、事务快照

事务快照(简称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  
向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI