码迷,mamicode.com
首页 > 数据库 > 详细

sqlserver事务隔离

时间:2018-10-02 20:36:11      阅读:178      评论:0      收藏:0      [点我收藏+]

标签:sql2012   alter   不提交   错误   pre   mit   alert   隔离   ack   

事务是一个工作单元,可能包含查询和修改数据以及修改数据定义等多个活动。我们可以显式或隐式的定义事务边界。可以使用BEGIN TRAN或者BEGIN TRANSACTION语句显式的定义事务的开始。如果希望提交事务,可以使用COMMIT TRAN语句显式的定义事务结束。如果不希望提交事务(即要撤销更改),可以使用ROLLBACK TRAN或者ROLLBACK TRANSACTION语句-摘抄自SQL Server 2012基础教程。

如果不显式的标记事务的边界,默认情况下,SQL Server将把每个单独语句作为一个事务,换句话说,默认情况下,每个单独语句结束后SQL Server自动提交事务。

说到事务就联想到并发,为了解决事务中的并发我们则不得不讨论下锁,所以接下来我们首先熟悉一下锁的模式-排他锁和共享锁。

先看看下面的试验,先开始一个事务处理,这个事务不提交也不回滚,然后另外在打开一个窗口查询数据。

技术分享图片

在上面的事务没有提交也没有回滚的情况下,如果另一个程序在执行查询,那么就会阻塞,如果没有设置查询超时,就会一直等待。

技术分享图片

原因:当事务会话对表进行修改(增删改)时,事务会请求数据资源的一个排他锁,这个锁直到事务完成(提交或者回滚)才会被取消。当读取数据时需要获取该资源上的共享锁,但是排他锁和共享锁不能兼容,此时会导致查询阻塞不得不进行等待。

就上面的试验,第一个事务拥有排它锁,在事务没有完成的情况下,第二个事务携带共享锁来访问表,此时就必须等待第一个事务完成后才能进行查询。但是反过来没问题,就是先有查询的事务进行查询,但是没完成事务,然后第二个事务来进行修改

并且能正常完成事务(这主要是因为在默认的隔离级别下,共享锁在完成数据的读取后就释放了,而不是在事务会话完成后释放)。注意,这个锁是针对修改的数据的行的,不是针对整个表。比如上面修改的是id=1的行,但如果我查询id=2的行,那么就不会出现阻塞。

因此,在事务处理中,如果遇到并发的情况,会有可能导致程序阻塞。

 

锁的隔离级别

SQL Server支持4个基于悲观并发控制的传统隔离级别:

  READ UNCOMMITTED、

  READ COMMITTED(企业内部部署的SQL Server实例的默认方式)、

  REPEATABLE READ、

  SERIALIZABLE。

SQL Server还支持两种基于并发控制(行版本)的隔离级别:

  SNAPSHOT

  READ COMMITTED SNAPSHOT(SQL Database的默认方式)

在某种意义上,SNAPSHOT和READ COMMITTED SNAPSHOT分别是READ COMMITTED和SERIALIZABLE的乐观并发对应方式。

技术分享图片技术分享图片

 

第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖。

脏读:一个事务读到另一事务未提交的更新数据。

虚读:一个事务读到另一事务已提交的新插入的数据。

不可重复读:一个事务读到另一事务已提交的更新数据。

第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一事务已提交的更新数据。

 

READ UNCOMMITTED隔离级别

READ UNCOMMITTED是最低隔离级别,在该隔离级别中,读取者不需要请求共享锁,不要求共享锁的读取者从不会与持有排他锁的写入者发生冲突,这意味着读取者可以写入者未提交的更改即脏读,也就是说,读取者不会干扰要求了排他锁的写入者,在该隔离级别下运行的写入者更改数据时,读取者可以读取数据。

技术分享图片

 默认情况下,上面的语句执行成功的,但是事务没有执行完成,这时排它锁是存在的,下面在另一个事务中设置隔离级别为READ UNCOMMITTED运行如下代码,可以看到,这个事务能查询到上一个事务没有提交的修改

 技术分享图片

这时候我们再将第一个事务回滚(只执行红框中的语句)。

 技术分享图片

可以看到在隔离级别是READ UNCOMMITTED的情况下,事务并发会出大问题的(第二个事务获取了一个错误的数据)。

 

READ COMMITTED隔离级别

 该隔离级别仅允许读取已提交的更改。它通过要求读取者获得一个共享锁来防止未提交的读取,也就是说,如果一个写入者持有了排他锁,读取者的共享锁请求将会与写入者的排它锁冲突,此时必须等待,一旦写入者提交了事务,读取者就可以获得它的共享锁,所以它必然是只读取提交后的修改。这个已经在本文最开始的时候试验过了。

在此隔离级别下要注意一种情况,就是:事务1有两个相同的查询,在刚执行完第一次查询后,事务2在这个时候对查询的数据进行了修改,并且提交了。那么事务1的第二个查询获取的数据就是新的数据。这一切看起来很正常,但是要注意的是,在事务1的两个

相同查询中获得了不同的数据,这种情况看似合理,但实则是每次读取到的值可能会有所不同,这种现象被称为不可重复读取或不一致解析,即既是虚读,又是不可重复读取。

更进一步则是在事务1中的第二次查询后,根据查询的结果来修改数据,并且成功提交了,这就是第二类更新丢失了。

REPEATABLE READ隔离级别

如果要解决虚读的问题,至少将隔离级别调到repeatable read。

看下面的试验,将隔离级别设置为repeatable read,在执行查询后,没有完成事务

技术分享图片

接着在另一个窗口中执行下面的修改事务,会看到事务一直在等待执行。这时候将上面的事务如果还有第二次读取,那么读取的结果和第一次肯定是相同的,因为没有被其他事务修改。提交或回滚后,下面的事务就立即执行 

技术分享图片

 通过这个试验可以看到, REPEATABLE READ隔离成功解决了不可重复读的问题,同时也解决了第二类更新丢失的问题。

 但是这个隔离会出现死锁的情况,看下面的试验:

事务中先执行查询,再执行修改

 技术分享图片

可以看到,这里死锁了,一直在执行,即使先修改后查询也是一样。原因:在这种隔离下,查询的时候会获取一个共享锁,这个共享锁必须直到事务完成后才释放(前面两种隔离不会这样),然后遇到了修改语句,需要获取一个排它锁,但是排他锁和

共享锁不能同时存在,因此就一直等着,死锁了。(前面两种隔离,如果在同一个事务中,先有修改,再有查询,那么也会造成死锁

 

还有一个问题,看下面的试验:

先执行事务:执行红框中的部分,表示这个事务只执行了一个查询,查询结果如下

技术分享图片技术分享图片

然后第二个事务来了:这个能成功执行,因为第一个事务的共享锁是锁定了id为1到6的行,但是id=8的行没有锁住,所以可以获得这行的排它锁。(不知道这样理解对不对)

技术分享图片

然后继续执行第一个事务的后面部分,查询结果

 技术分享图片

可以看到,同一个事务的两次同样的查询,获得了不同的结果,这就是虚读。

 

ERIALIZABLE隔离级别

为了防止幻影读取,需要将隔离级别提升为SERIALIZABLE,最重要的部分是SERIALIZABLE隔离级别的行为类似于REPEATABLE READ即它要求读取者获取一个共享锁来进行读取,并持有锁到事务结束,但是SERIALIZABLE隔离级别添加了另外一个方面-在逻辑上,该隔离级别要求读取者锁定查询筛选所限定的键的整个范围。这意味着读取者锁定的不仅是查询筛选限定的现有行,也包括将来行,或者准确地说,它会阻止其他事务尝试添加读取者查询筛选限定的行。

基于行版本的隔离级别

在SQL Server中存在两种基于行版本控制技术的隔离级别:SNAPSHOT、READ COMMITTED SNAPSHOT。将提交行之前的版本存储在tempdb中,SNAPSHOT隔离级别在逻辑上类似于SERIALIZABLE隔离级别,READ COMMITTED SNAPSHOT隔离级别类似于READ COMMITTED隔离级别,但是,读取者使用基于行版本控制的隔离级别并不不会发出共享锁,所以在请求的数据以排他锁锁定时它们不会等待,读取者仍旧会获得类似于SERIALIZABLE和READ  COMMITTED的一致性级别,如果当前版本不是它们希望看到的版本,那么SQL Server会给读取者提供一个较旧的版本。

如果启用了任何基于快照的隔离级别,在修改tempdb之前,DELETE和UPDATE语句需要复制行的版本,对于INSERT语句则不需要再tempdb中版本化,因为它不存在早期的版本,但需要注意的是,启用任何基于行版本控制的隔离级别对于数据更新和删除的性能可能会有负面影响,由于它们不会获取共享锁,并且哎数据被以排他方式锁定或是数据版本不是所期望的版本时不需要等待,因此对于读取者的性能通常会有所改善。

 

SNAPSHOT隔离级别

要想在企业部署的SQL Server实例中允许事务以SNAPSHOT隔离级别工作,首先需要在查询窗口执行以下代码打开快照隔离级别。如下:tsql2012是数据库名

ALTER DATABASE TSQL2012 SET ALLOW_SNAPSHOT_ISOLATION ON

下面通过试验来看看SNAPSHOT的行为:

执行下面的 事务,事务未完成。这如果没有执行刚才的alert语句,这个事务是会死锁的,原因上面已经解释过了

技术分享图片

 

然后另一个事务:

技术分享图片

 

从查询结果看,获取的数据是第一个事务修改前的数据。然后现在将第一个事务提交,再来看看第二个事务的后半部分的查询结果

 看到,只要第二个事务没有提交,那么它不管进行多少次查询,获得的结果都是一样的

 技术分享图片

 

 

 SNAPSHOT隔离级别可以防止更新冲突,但不会像REPEATABLE READ和SERIALIZABLE隔离级别那样产生死锁,SNAPSHOT隔离级别的事务失败,表明检测到了更新冲突,SNAPSHOT隔离级别通过检查存储的版本来检测更新冲突,它可以发现在事务的读取和写入之间是否有另一个事务修改了数据。

READ COMMITTED SNAPSHOT隔离级别

该隔离级别也是基于行版本控制,它与SNAPSHOT隔离级别区别在于,读取者获得是【语句】启动时可用的最后提交的行版本,而不是【事务】启动时可用的最后提交的行版本,READ COMMITTED SNAOSHOT也不会检测更新冲突,导致类似于READ COMMITTED隔离级别,但在所请求资源以排他锁锁定时,不会请求共享锁并且不会等待。在企业内部部署的SQL Server中要想启动READ COMMITTED SNAPHOST隔离级别,需要打开唯一会话来设置,否则无法进行启用(启用该隔离级别实际上是将READ COMMITTED隔离级别在语义上改变为READ COMMITTED SNAPSHOT隔离级别)。

ALTER DATABASE TSQL2012 SET READ_COMMITTED_SNAPSHOT ON;

这个试验没成功,暂时不说了

 

sqlserver事务隔离

标签:sql2012   alter   不提交   错误   pre   mit   alert   隔离   ack   

原文地址:https://www.cnblogs.com/jin-/p/9737647.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!