首页 > 编程知识 正文

数据库的事务隔离级别,事务隔离级别是由什么实现的

时间:2023-05-06 10:36:59 阅读:47188 作者:3700

简介:作者:纯情龙猫/crdhlg荣

一谈到事务隔离水平,开发的同学都可以说同样的话。 肮脏的读法、不可重复的读法、RC、RR .等常用术语也大致知道意思。 但是,制作技术,严格和纤细是很重要的。 如果对事务隔离级别的认识仅停留在众所周知的程度,则数据库核心开发人员可能会开发对用户来说难以理解的隔离级别表示,业务开发人员可能会从数据库中检测到与预期不同的结果。

那么,你如何判断自己是否深入了解了事务隔离水平? 开发的同学可以问自己这样两个问题。 (1)事务隔离水平可分为几类? 各自能解决什么问题? 有明确的定义吗? 这样的定义正确吗? )2)当前主流数据库(Oracle/MySQL . )的隔离级别表现和实现是什么? 与“公式”的定义一致吗?

如果能明确回答这两个问题,恭喜你。 你对事务隔离水平有非常深刻的认识。 如果做不到的话,没关系。 读了正文后会得出答案。

1 .事务隔离级别保障事务隔离级别,主要是关系数据库的ACID特性的隔离(I ),为有冲突的并发事务提供一定程度的安全保证。 ansi (美国标准版) SQL 92标准(http://www.contrib.Andrew.CMU.edu/~ shadow/SQL/SQL 1992.txt

http://www.Sina.com/SQL-transaction t1 modifies arow.SQL-transaction T2 thenreadsthatrowbeforet1performsacommit.if t1 thethether T2 willhavereadarowthatwasnevercommittedandthatmaythusbeconsideredtohaveneverexisted.http://www.Sina.com/SQL-transaction t1 reads arow.SQL-transaction T2 thenmodifiesordeletesthatrowandperformsacommit.if t1 thenattemptstorererereadthe iscoverthattherowhasbeendeleted.http://www.Sina.com/SQL-transaction t1 readsthesetofrowsnthatsatisfysome.SQL-transaction T2 thenexecutessql-statementsthatgenerateoneormorerowowstior . if SQL-transaction t1 thenrepeatstheinitialreadwiththesame,itobtainsadifferentcollectionof

讨厌以上定义冗长,可以直接看到以下形式化的描述。

a1 dirty read:w1 [ x ] . R2 [ x ] . (a1 and C2 inanyorder )

a2 fuzzy read:R1 [ x ] . w2 [ x ] . C2 . R1 [ x ] . C1

a3 phantom read:R1 [ p ] . w2 [ yinp ] . C2 . R1 [ p ] . C1

其中w1[x]指示事务1正在写入记录x,r1指示事务1正在读取记录x,c1指示事务1已提交,a1指示事务1正在回滚表示事务1根据谓词p的条件读取多个记录,w1[y in P]表示事务1写入记录y满足谓词p的条件。

由此,ANSI定义了4种隔离级别,分别解决了上述3种异常:

根据这几个异常现象定义隔离级别可以说并不严格,但Jim Gray著名论文acritiqueofansisqlisolationlevels (稍后简称critique )对此进行了批评。

33558www.Sina.com/:p1/p2/p3的事务被禁止,满足了可序列化级别。 但是,ANSI标准规定,序列化级别为“多个并发事务执行的效果等效于某种序列化执行的效果”。 这两者明显矛盾,禁止P1/P2/P3事务不一定“等效于某种串行执行”。 因此,Critique将禁止ANSI定义的P1/P2/P3的隔离水平称为Anomaly Serializable。

Dirty read: :异常现象定义不准确,以下范例未涵盖在A1中,但出现了dirty read (tx N2读为x y!=100 )。 同样,A2/A3也可以举出这样的例子。 感兴趣的同学可以自己列举一下。 这里不详细说明。

究其原因,ANSI对异象的定义太为严格,如果除去对事务提交、回滚和数据查询范围的要求,仅保留关键的并发事务之间读写操作的顺序,更为宽松且准确的异象定义如下:

P1 Dirty Read: w1[x]...r2[x]...(c1 or a1)
P2 Fuzzy Read: r1[x]...w2[x]...(c1 or a1)
P3 Phantom: r1[P]...w2[y in P]...(c1 or a1)

不严谨之三:三种异象仅针对S(ingle) V(alue)系统,不足以定义M(ulti)V(ersion)系统的隔离性。很多商业数据库所实现的SI,未违反P1、P2和P3,但又可能出现Constraint violation,不可串行化。除了P1/P2/P3,还可能出现哪些异常呢?

P4 Lost Update:r1[x]...w2[x]...w1[x]...c1
A5A Read Skew:r1[x]…w2[x]... w2[y]…c2…r1[y] …(c1 or a1)
A5B Write Skew:r1[x]…r2[y]…w1[y]…w2[x]…(c1 and c2 occur)
A5B2 Write Skew2:r1[P]... r2[P]…w1[y in P]…w2[x in P]...(c1 and c2 occur)

对这四种情况,分别举一个例子:

r1[x=50] r2[x=50] w2[x=60] c2 w1[x=70] c1

Lost Update:事务1和事务2同时向同一个账户x分别充20和10块,事务1后提交,将70块写入数据库,事务2提交结果60块被覆盖。正确的情况下,事务1和2提交成功,账户里应该有80块。

(x+y=100) r1[x=50] w2[x=10] w2[y=90] c2 r1[y=90] c1

Read Skew: x和y账户分别有50块钱,加起来共100块。事务1读x(50块)后,事务2将x账户的40块转到y账户,事务2提交后,事务1读y(90块)。在事务1看来,x+y=140,出现了不一致。

(x+y>=60) r1[x=50] r2[y=50] w1[y=10] c1 w2[x=10] c2

Write Skew:x和y账户分别有50块钱,加起来共100块。假设存在某种约束,x和y账户的钱加起来不得少于60块。事务1和事务2在自认为不破坏约束的情况下(分别读了x账户和y账户),再分别从y账户和x账户取走40。但事实上,这两个事务完成后,x+y=20,约束条件被破坏。

(count(P)<=4):r1[count(P)=3],r2[count(P)=3],insert1[x in P],insert2[y in P],c1,c2,

Write Skew2:将Write Skew的条件改为范围。

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:事务提交或回滚后释放锁。

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

其中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),会被阻塞,直到读事务提交或回滚。

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

然而当今数据库基于性能等多方面考虑,很少有完全基于锁实现隔离级别的,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生成相应的一致性版本页面。

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

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

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

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

2.3 MySQL(InnoDB)隔离级别实现

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

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

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

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

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

3.MySQL (X-Engine) 隔离级别实现

X-Engine 隔离级别实现同样采用MVCC+Lock的方式,支持RC和SI,表现与Oracle的RC,Serializable一致。具体实现层面,X-Engine 实现了行级MVCC,每条记录的key都附有一个 Sequence 代表自己的版本。所有的读操作均是快照读(包括加锁读),读请求所需要的snapshot也是一个Sequence 。写写冲突处理依靠两阶段锁,并遵循First committer wins。

按照惯例,以下面两个例子分析,说明我们的实现原理:


与Oracle类似,X-Engine SI隔离级别,可以避免Lost Update:

4.总结

前文介绍了多种数据库隔离级别的表现,对比如上表所示。其种MySQL比较特殊,如前文所述,其RR级别可以禁止部分幻读现象。开发人员在使用数据库时,需要注意:尽管不同数据库隔离级别名称相同,但是表现却可能存在差异。

原文链接:https://developer.aliyun.com/article/766148?

版权声明:本文中所有内容均属于阿里云开发者社区所有,任何媒体、网站或个人未经阿里云开发者社区协议授权不得转载、链接、转贴或以其他方式复制发布/发表。申请授权请邮件developerteam@list.alibaba-inc.com,已获得阿里云开发者社区协议授权的媒体、网站,在转载使用时必须注明"稿件来源:阿里云开发者社区,原文作者姓名",违者本社区将依法追究责任。 如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:developer2020@service.aliyun.com 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。