事务概念
事务:由一系列数据库操作组成的一个完整的逻辑过程。例如银行转帐,从原账户扣除金额,以及向目标账户添加金额,这两个数据库操作的总和,构成一个完整的逻辑过程,不可拆分;事务具有ACID特性。
尽管在ACID的保护下,数据库中并发事务的执行有时会产生不一致的情况。
在关系数据库中,多个会话可以同时访问同一个数据库中用一个表的同一个元组,意味着在同一时间段有多个会话可以对其施加读写操作,而读写顺序的不同会对事务产生不同的影响,这三种因素叠加在一起,会存在几种对数据库有不同影响的情况:
读-读操作:如果同时只存在多个读操作,对于数据本身是没有影响。
读-写操作:如果读写操作都存在,因写在前读在后(脏读)、读在前写在后(不可重复读)或者读-写-读(幻读),可能因数据被写而导致另一个读事务的会话读取到错误的数据。
写-写操作:如果同时存在多个写-写操作,此时会改变数据在同一时刻的语义,避免此类并发操作;
并发事务读写异常
下图为SQL标准定义的三种读异常以及写异常
1 读异常
1.1 脏读是指一个事物读取到另一个事务未提交的数据。
事务T1在t0时刻对row进行更新操作,T2事务在t1时刻读取到的是事务T1修改后该row的值,但是事务T1在t2时刻终止此事务使得其对之前的修改操作失效。如果数据库引擎不支持并发控制以避免异常,则T2读取到的值就是T1修改后的值,显然这个值是现实世界不存在的。即事务T2发生的脏读
1.2 不可重复读是指事务再次读取之前读取过的数据时,发现该数据已被其他提交事务所更改
事务T1在t0时刻读取row的旧值,事务T2在t1时刻对该row进行修改操作后提交该事务使其生效。那么事务T1在t3时刻再次读取row的数据时,发现物(还是row对象)是人非(值发生了改变)。即事务T1发生了不可重复读异常
1.3 幻读是一个事务前后读取同一特定条件的数据集,后者读取却发现数据集中的内容被其他事务所更改。
事务T1在t0时刻读取指定条件的row数据,事务T2在t1时刻插入新的数据或者更新旧数据但满足事务T1所指定的WHERE条件;事务T1在t3时刻再次读取相同where条件下的row数据,发现有新加入的行(物是人非)。即事务T1发生了幻读异常
2 写异常
2.1 丢失更新
按照时间顺序,事务T1在t0时刻读取row数据(data-1),事务T2在t1时刻更新row(data-2),事务T1在t2时刻对row进行修改(data-3),如果没有并发控制,row最终所对应的数据为data-3.但是事务T1在t3时刻提交使得T2对row的修改失效。对于事务T1而言,覆盖掉了不是自己修改的数据,即事务T1引发了丢失更新异常现象。
2.2 脏写
按照时间顺序,事务T1在t0时刻对row进行了修改,事务T2在t1时刻页也对row进行修改,如果没有并发控制,T2的修改会生成新值,但是T1在t3时刻回滚使得T2对row修改失效,对于T1而言:回滚掉的不是自己修改数据,即事务T1上发生脏写现象。
2.3 写偏序
隔离界别与三种读异常示意图
并发控制技术
在数据库中多个事务可能会访问同一数据项,为保证事务的ACID特性,必须采用某种方式高效地调度并发事务,该技术称为并发控制技术。
并发控制技术的实现策略可分为乐观并发控制与悲观并发控制,这两种控制思想是从“何时检测冲突”的角度定义的。
1 乐观(OCC):从一开始,每一项操作都允许进行,但在事务提交的时候,会进行隔离性与完整性约束的检查,如果有违反则终止事务。在冲突较少的情况下,乐观并发控制方法是合适的。
1 乐观(PCC):从一开始,即检查每一项操作是否违反隔离性与完整性约束,如果可能违反,则阻塞该操作。如两阶段封锁技术中读锁阻塞另一个事务的写操作,是因为写操作可能造成上述提到的读异常。因此两阶段封锁技术属于悲观的方法,提前做出预防。
接下来主要讲解两阶段封锁技术和MVCC多版本并发控制技术。
两阶段封锁技术:
MVCC多版本并发控制技术:本质是为元组保留多个不同的版本,读写操作作用于不同的版本,从而做到读不阻塞写、写不阻塞读(相比于两阶段封锁技术进一步提升了并发能力)。而postgres中MVCC的实现方式之一为快照隔离
快照隔离的核心思想是: 每个事务都从一个数据库的快照中读数据。 (即:事务看到的所有数据,都是在事务开始的时间点之前 committed 的数据。) 如果有些数据在当前事务开始之后,被其他事务改变了值,快照隔离能够保证当前事务无法看到这个新值。在快照隔离中每一次读都是从一个过去的快照中读取的,因此不会出现多次读一个值却读到不一致结果的情况。
注:快照隔离技术会发生写偏序异常问题,会导致事务的不可串行化,可以通过可串行化快照隔离SSI技术解决此问题,内容很晦涩难懂有幸相关知识后续讲解。