目录
关于事务概述与隔离级别等基础内容的详细信息,将会另开一篇专讲。
此篇适合有一定数据库基本概念的朋友查看。
一、PG事务的隔离级别
PG只支持3种隔离级别:读提交、可重复读、串行化,不支持读未提交。
二、MVCC介绍
1、并发控制
并发控制是一种在数据库中并发运行多个事务时,保持一致性和隔离性的机制,这是ACID的两个属性。
并发控制技术有三种:
(1)多版本并发控制:MVCC
(2)严格的两阶段锁:S2PL
(3)乐观并发控制:OCC
这一小节主讲MVCC
2、MVCC的特点
(1)每次写操作都会创建数据项的新版本,同时保留旧版本;
(2)当事务读取一个数据项时,系统会选择其中一个版本,以确保单个事务的隔离。
MVCC的主要优点有:读不阻止写,写不阻止读;
相反,基于S2PL的系统必须在写卡器写入项时阻止读卡器,因为写卡器获取项的独占锁。
而PG和一些rdbms使用MVCC的一个变体,称为快照隔离(Snapshot Isolation,SI);
PG通过应用可见性检查规则来选择项目的适当版本;
由于 PG数据块中包含了未删除和已删除的行的数据,所以在读取数据块中的行的时候,需要一套规则来判断,哪些行能够被哪些事务所看得见,我们成为行可见性规则。
关于可见性规则的具体内容,将在二十章:可见性规则 中详细展开
Oracle使用回滚段来选择项目的适当版本。
Oracle专门创建了一个回滚表空间,用来存放修改前的行的数据,而表的数据块中没有包含删除行的数据,所以不需要行可见性规则来判断
三、CLOG的工作原理
clog:commit log,提交日志,记录了事务的状态(In_Progress、Committed、Aborted、Sub_Committed等)
在T1时刻:提交了事务ID为200的事务,则在clog中,将事务200的状态修改为commited,此时事务200对应的数据,状态为可见;
在T2时刻:回滚了事务ID为201的事务,则在clog中,将事务201的状态修改为Aborted,此时事务201对应的数据,状态为不可见。
四、事务快照
事务快照:Transaction Snapshot
1、事务快照的概念
事务快照是一个数据集,用于存储有关单个事务在某个时间点上是否所有事物都处于活动状态的信息,在这里,活动事务表示它正在进行或尚未启动。
2、事务快照内置函数
txid_current_snapshot
函数的文本表示格式为:$xmin:$xmax:$xip_list
xmin:最早仍在活动的txid。所有以前的事务要么提交并可见,要么回滚并停止;
xmax:第一个尚未分配的txid。截至快照时,所有大于或等于此值的txid尚未启动,因此不可见;
xip_list:快照时的活动txid。该列表仅包含xmin和xmax之间的活动tid;
例如,在快照“100:104:100,102”中,xmin是100,xmax是104,xip_list是'100,102′,而101&103已完成。
3、不同隔离级别的事务快照状态
事务管理器中,不同隔离级别的事务快照状态,如下图所示:
T1时刻:事务A启动,分配事务ID200,此时快照号为200:200:,活动的事务号为200;
T2时刻:事务B启动,分配事务ID201,由于事务A尚未结束,此时快照号依旧为200:200:,活动的事务号为200、201;
T3时刻:事务C启动,分配事务ID202,由于事务AB都未结束,此时快照号依然为200:200:,活动的事务号为200、201、202;
T4时刻:事务A提交,则活动的事务号变为201、202,此时,往后的时间查看快照号,都变为201:201。
4、并发update如何防止更新的数据丢失
并发 UPDATE操作,隔离级别不同,如何保护已修改的数据不丢失:
假如事务B的隔离级别处于:可重复读、串行级别时,事务B的执行结果有以下几种情况:
(所有情况皆基于修改同一个行的时候)
- 事务A回滚:事务B更新成功;
- 事务A提交:假如事务B查询了表,则再次更新时失败,如果没有,则会更新成功,图示详情如下:
四、可串行化隔离级别特点
1、SSI-可串行化快照隔离
在PG中实现SSI的两个重要概念:
---SIREAD locks:SIREAD锁在内部称为谓词锁,包含三个部分:一对对象和(虚拟)txid;
---rw-conflicts(rw冲突):rw-conflicts是SIREAD锁的三个组成部分中的一个+读写SIREAD锁的两个txid;
(1)读写倾斜情况导致的SSI
读写倾斜:
过程 事务A读对象B,事务B读对象A,随后,事务A写对象A会成功,事务B写对象B会失败;
过程 事务A读写对象B,事务B读写对象A,循环执行 会成功。
这种情况导致事务B提交失败,是为了保护事务A修改的结果,因为事务B是在可串行化的事务隔离级别,无法看到事务A修改后的结果。
还有一些其他情况的SSI,例如:
(2)在事务A提交后执行事务B对同一行的update
(3)在事务A提交后执行事务B的select
2、假阳性-导致SSI情况出现异常的情况
假阳性指的是,在一些特殊的情况下,虽然处于高安全级别,但数据库实际并没有触发SSI 的情形。
(1)常见的假阳性
假如两个事务分别查询&更新各自的行,则互不影响,两个事务都可提交成功。例如:
事务A和事务B都只针对表tbl的特定行进行查询,查询的行都不一样,且只修改自己查询过的行,则即使是串行化,也不会执行失败
index scan using the difference index page:两个索引使用了不同的索引页 的情况就是典型的假阳性。
3、假阳性异常的情况
(应该是假阳性,但是触发了SSI)
(1)假阳性异常的情况1
假阳性有一个前提:查询对象时没有发生全表扫描。
假如查询的对象没有索引,事务A和事务B在查询特定行时,走的是全表扫描,则即使查询结果中没有出现对方事务的行,实际上本事务是已经访问过的(实现了交叉访问的情形),这种情况即使看着像假阳性,但也会导致SSI,报错会发生在事务B执行commit时;
(2)假阳性异常的情况2
index scan using the same index page:两个索引使用了相同的索引页
注:在表有索引的情况下,也分两个情形:
- 表比较大的时候,假如两个事务都走了索引,但是两个事务的索引恰好都读取了相同的页上不同的行,也会导致假阳性异常,触发SSI。因为对于数据库来说,页是最小的读取单位。索引读的时候会把行所在的页上所有的行都读到内存里,导致SSI。
- 表过于小的时候,会导致ROOT和LEAF索引块同属于一个块,两个事务也会发生交叉访问同一个索引块,从而触发SSI。