首页   >   S   >
    事务的隔离级别不包括

事务的隔离级别不包括

2020-09-03

2.隔离级别实现

上一节介绍了ANSI定义的3种异象,及根据禁止异象的个数而定义的事务隔离级别。因为不存在严格、严谨的“官方”定义,各主流数据库隔离级别的表现也略有不同,一些现象甚至让用户感到困惑。我认为相较于纠结隔离级别的准确定义,认识各数据库隔离级别的表现和实现,在生产环境中正确的使用它们才是更应该关注的事情。本节将以大篇幅具体的例子为切入点,介绍几种主流数据库隔离级别的表现,及内部对应的实现。

2.1 Lock-based 隔离级别实现

在展示Lock-based隔离级别实现前,先介绍几个与锁相关的概念:

Item Lock:对访问行加锁,可以防止dirty/fuzzy read。
Predicate Lock(gap lock):对search的范围加锁,全表扫描直接对整张表加锁,可防止phantom read。
Short duration:语句结束后释放锁。
Long duration:事务提交或回滚后释放锁。

上述锁操作组合,便可实现不同级别的事务隔离标准,如下表所示。

image.png

其中S lock代表共享锁,X lock代表排它锁。

首先所有写操作加X locks时,都会选择Long duration,否则short duration锁被释放后,在事务提交前该条更改可能被其它事务写操作覆盖,造成脏写(dirty write)。

其次对于读操作:

Short duration Item S lock 禁止了 P1发生,读操作如果遇到正在修改的行(写事务加了X Lock),阻塞在S Lock,直到写事务提交。

Long duration Item S lock 禁止了P2发生,写操作遇到读事务(S Lock),阻塞在X Lock上直到读事务提交或回滚。

Long duration Predicate/Table S Lock 禁止了P3发生,(范围)写操作遇到范围读操作(加Predicate S Lock),会被阻塞,直到读事务提交或回滚。

基于锁实现的三种隔离级别分别能禁止的异象如下表所示:

image.png

然而当今数据库基于性能等多方面考虑,很少有完全基于锁实现隔离级别的,MVCC+Lock的方式,可以满足读请求不加锁,是主流的实现方式。

2.2 Oracle隔离级别的实现

Oracle仅支持两种隔离级别:Read Committed与Serializable。尽管官方这样描述,Oracle的Serializable实际是基于MVCC+Lock based的SI(Snapshot Isolation)隔离级别。

为实现快照读,内部维护了全局变量SCN(System Commit/Change Number),在事务提交时递增。读请求获取Snapshot便是获取当前最新的SCN。Oracle实现MVCC的方式是将block分为两类:(1)Current blocks为当前最新的页面,与持久化态数据保持一致。(2)Consistent Read blocks,根据snapshot SCN生成相应的一致性版本页面。

以下两个具体的例子展示了:不同隔离级别下,读写语句在数据库内部发生了什么。

image.png

Oracle在read committed隔离级别下,每条语句都会获取最新的snapshot,读请求全部是snapshot读。写请求在更新行之前,需要加行锁。由于写操作不会因为有其它事务更新了同一行,而停止更新(除非不满足更新的谓词条件了),因此Lost Update有可能发生。

image.png

Oracle在serializable隔离级别下,事务开始便获取snapshot。读请求全部是snapshot读,而写请求在更新行之前,需要加行锁。写操作在加锁后,首先检查该行,如果发现:最近修改过这行的事务的SCN大于本事务的SCN,说明它已经被修改且无法被本事务看到,会做报错处理,避免了Lost Update。这种写冲突的实现,显然是first committer wins。

下表展示了Oracle的两种隔离级别,分别能够避免哪些异象:

image.png

2.3 MySQL(InnoDB)隔离级别实现

InnoDB同样以MVCC+Lock的方式实现隔离级别。其中普通select语句均是snapshot read。而delete/update/select for update等语句是加锁实现的current read,如下表所示(注:该表为Pecona 5.6版本的代码实现)。

image.png

InnoDB的RC隔离级别的表现与Oracle相似。而相较于Oracle的SI,InnoDB RR隔离级别依旧不能避免Lost Update(例如下例)。究其原因,InnoDB在RR隔离级别下,不会在事务提交时判断是否有其它事务修改过该行。这避免了了SI更新冲突带来的回滚代价,带来了可能发生Lost Update的风险。

image.png

由于update等操作均是加锁的当前读,因此Phantom Read的现象也是存在的(如下表所示)。但是如果将Txn1的update语句替换为select语句,Phantom Read现象则可以禁止,因为整个事务select语句使用的是同一个snapshot。

image.png

Innodb RR的实现方式虽然并非并未严格排除Lost Update和Repeatable Read,但其充分利用MVCC读不加锁的并发能力,同时current read避免了SI在更新冲突剧增时过多的回滚代价。

InnoDB还实现了Lock Based Serializable(详见2.1),禁止了所有异象。

展开阅读全文 icon
其他推荐内容