zoukankan      html  css  js  c++  java
  • Sql Server中的事务隔离级别

    数据库中的事物有ACID(原子性,一致性,隔离性,持久性)四个特性。其中隔离性是用来处理并发执行的事务之间的数据访问控制。SqlServer中提供了几种不同级别的隔离类型。

    概念          

    Read UnCommitted

    当前事务可以读取其他事务已修改但还未提交的数据。如果其他事务进行数据Rollback,当前事务就会出现脏读,数据错误。

    Read Committed

    当前事务只能读取其他事务修改并且已提交的数据。这样避免了脏读。但是如果其他事务在当前事务两次读之间对数据修改,会导致当前事务两次相同读取操作得到的结果不一样。

    Repeatable Read

    在Read Committed基础上,当前事务在对相同数据的多次读之间,其他事务不能对数据进行修改。因此也就不会出现Read Committed中的多次读取数据不一致的情况。但是该类型下还会有一个问题,在两次数据读取之间,其他事务还是可以插入新的数据的,这样可能导致当前事务两次读取的数据数量不一样。

    Serializiable

    这个算是终极隔离级别了,在Repeatable Read的基础上,当前事务的两次数据读取之间,其他事务不能插入会出现在当前事务数据读取操作Where条件所表示范围内的数据。这样当前数据的两次相同的数据操作就不会返回不同数目的数据了。

    新版的SQLServer中还有Snapshot隔离级别,以后有机会再研究。

    实现原理        

    Sql Server中,事务隔离是通过锁的机制来实现的。算是我们所说的悲观并发的处理方式。

    Read UnCommitted

    这种类型下,Sql Server不会对数据加任何锁(写操作除外)。因此数据的读写就没有任何限制了。

    Read Committed

    这是Sql Server的默认事务隔离级别。其他事务进行写操作时,会对数据加排他锁知道事务结束;当前事务对数据进行读操作时,会对数据加共享锁(共享锁与排他锁冲突),因此如果有事务修改了数据没有提交,当前事务则无法获取到共享锁,而无法读取数据。这样解决脏读的问题。因为该级别下获取的共享锁在数据读取到后即失效,因此会出现重复读带来的问题。

    Repeatable Read

    在Read Committed级别的基础上,该级别下,读数据时的共享锁的有效期延长到了事务结束,因此这期间其他事务无法拿到排他锁对数据进行修改。因此解决了重复读的问题。

    Serializiable

    在Repeatable Read基础上,该级别读数据的时候会针对所读数据的范围(而不是只对读取到的数据)加共享锁。因此其他事务无法在该范围下插入数据。

    存在问题        

    1. Repeatable Read和Serializiable解决了数据一致性的问题,但是牺牲了数据的并发访问能力。

    2. Repeatable Read和Serializiable延长了共享锁到事务结束,这样会导致多个事务都拿到共享锁,但当它们都想获取数据的排他锁进行修改时,会形成死锁。因为互相都在等待对方释放共享锁。这种情况下的解决方案是如果在事务中需要对数据进行修改,改为获取更新锁(同一时间只有一个事务能拿到相同数据的更新锁)。这样就不会出现死锁的情况了。

    SQL          

    在Sql Server中,可以通过SET TRANSACTION ISOLATION LEVEL命令来更改事务隔离类型。设置后统一数据库连接下会一直使用该隔离类型。但是如果在存储过程中设置隔离类型,存储过程在返回的时候,隔离类型恢复为调用存储过程之前的状态。因此在存储过程中设置的隔离类型只对该存储过程执行过程有效

    验证          

    Read UnCommitted

    有如下数据:

    ID Name Age
    1   Je   3

    在事务1中执行如下SQL

    BEGIN TRAN
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    UPDATE Account SET AGE = 4 WHERE ID = 1
    //此处没有提交事务

    在事务2中再执行如下SQL

    BEGIN TRAN
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
    SELECT * FROM Account WHERE ID = 1
    COMMIT

    会得到如下结果

    ID Name Age
    1   Je   4

    因此该隔离级别下,能读到未提交的修改。

    Read Committed

    将上面的代码中的隔离级别改为READ COMMITTED。得到的结果为事务2在读取数据时为一直处于等待状态。

    执行如下代码查看锁的状态。

    SELECT * FROM sys.dm_tran_locks where resource_type != 'DATABASE'

    会发现事务2一直在等待获取共享锁。此时在事务1中执行COMMIT命令,事务2则可以继续执行,并且能拿到事务1提交的数据。

    ID Name Age
    1   Je   4

    接下来按顺序执行如果代码

    首先重置ID为1的数据的Age为3.

    事务1

    BEGIN TRAN
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    SELECT * FROM Account WHERE ID = 1
    WAITFOR DELAY '00:00:10'
    SELECT * FROM Account WHERE ID = 1
    COMMIT

    事务2

    BEGIN TRAN
    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    UPDATE Account SET AGE = 4 WHERE ID = 1
    COMMIT
    GO

    事务1会得到如下结果:

    ID Name Age
    1   Je   3

    ID Name Age
    1   Je   4

    两次读的结果不一样

    Repeatable Read

    上面的代码如果设置为Repeatable Read隔离类型,得到如下结果(先将行数据重置为age=3):

    ID Name Age
    1   Je   3

    ID Name Age
    1   Je   3

    两次读操作的age值都为3,因为在事务1完成之前,事务2的写操作被block

    如果执行如下代码:

    事务1

    BEGIN TRAN
    SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
    SELECT * FROM Account WHERE ID >= 1;
    WAITFOR DELAY '00:00:10';
    SELECT * FROM Account WHERE ID >= 1;
    COMMIT

    事务2

    BEGIN TRAN
    SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
    INSERT INTO Account values('Je2',3)
    COMMIT
    GO

    事务1最终会得到如下结果

    ID Name Age
    1   Je   3

    ID Name Age
    1   Je   3

    2   Je2      3

    两次读结果的数量不一样。

    Serializiable

    如果将事务隔离级别设为Serializiable得到结果是

    ID Name Age
    1   Je   3

    ID Name Age
    1   Je   3

    如果在事务1执行期间查看锁的情况。会发现事务1获得的锁的类型为RangeS-S。即范围锁。

  • 相关阅读:
    scheduletask任务调度(2间隔时间)
    scheduletask任务调度
    初始webservice
    ssh整合
    aop
    自定义框架(MyMvc)
    数据校验
    原生态ajax
    struts2国际化
    文件下载
  • 原文地址:https://www.cnblogs.com/Code-life/p/9942057.html
Copyright © 2011-2022 走看看