一、事务隔离级别控制着事务的如下表现:

  1. 读取数据时是否占用锁以及所请求的锁类型。
  2. 占用读取锁的时间。
  3. 引用其他事务修改的行的读操作是否:
    • 在该行上的排他锁被释放之前阻塞其他事务。
    • 检索在启动语句或事务时存在的行的已提交版本。
    • 读取未提交的数据修改。

以上说明事务隔离级别主要针对读操作来说的。(DML语句我们可以不考虑事务隔离级别,因为任何事物隔离级别下DML的加锁都很严格,属于得不到就等待的类型)

二、脏读、不可重复读、幻读的区别:

提到事物隔离级别就不能不提这3个概念,可以说事务隔离级别就是为了避免这3种情况出现的。

脏读:读到了其他事务已修改但未提交的数据

不可重复读:由于其他事务的修改,导致同一事务中两次查询读到的数据不同

幻读:由于其他事务的修改,导致同一事务中两次查询读到的记录数不同

可能有人对幻读和不可重复读的定义不太理解,两者最大的区别实质上在于加锁的不同,后边会有讲解。

三、ANSI/ISO标准定义了下列事务隔离级别,SQL Server数据库引擎支持全部这4种隔离级别:

因此四种隔离级别与脏读、幻读、不可重复读的对应情况如下:

需要特别提醒的是:虽然Mysql、Oracle所支持的事务隔离级别也基本遵循ANSI标准,但却有很大区别:

  • Oracle只支持已提交读和序列化读。
  • Mysql默认的的可重复读隔离级别通过范围锁实现了避免幻读。

四、除以上4种隔离级别外SQL Server还支持使用行版本控制的其他两个事务隔离级别:

  • 一个是默认的read committed隔离级别下的snapshot实现,严格来说并不算一个事务隔离级别,只是read committed的一个特殊形态。

  • 一个是全新的事务隔离级别----快照隔离级别。

两者的开启方式为:

1、如果要开启SNAPSHOT事务隔离级别,需要预先设置ALLOW_SNAPSHOT_ISOLATION为ON,且目前只能修改会话级别的事务隔离级别。

ALTER DATABASE [dbname] SET ALLOW_SNAPSHOT_ISOLATION ON; --需要单用户模式下修改,因为要加库级别的独占锁。
 然后执行如下语句修改事务隔离级别:(修改后只在会话级别生效,无法修改全局级别的事务隔离级别)
SET TRANSACTION ISOLATION LEVEL
{ READ UNCOMMITTED
| READ COMMITTED
| REPEATABLE READ
| SNAPSHOT
| SERIALIZABLE
}

2、使用READ_COMMITTED_SNAPSHOT,则直接执行下列ALTER语句修改,是在默认的READ COMMITTED隔离级别下修改的,此隔离级别修改后永久生效,使用dbcc useroptions查看可以看到事务隔离级别被全局的修改成了read committed snapshot。

ALTER DATABASE [dbname] SET READ_COMMITTED_SNAPSHOT ON;

两者的区别在于:

READ_COMMITTED_SNAPSHOT是指Select语句总是读取最新的已提交的数据,即如果有DML事务正在执行,那么select语句不会被阻塞而是读取这些DML事务预先生成的前镜像,这种读只会在表上加Sch-S锁,其他的行锁页锁全部没有。DML数据一旦提交,再次执行Select语句就会立马读到新的数据。

SNAPSHOT隔离级别与上述的区别在于,如果你在同一个事务内执行两次相同的select语句,那么即便在这两次select语句之间发生了数据更改且提交,两次读到的数据也是一样的。

用官网的一句话来描述两者区别就是:READ_COMMITTED_SNAPSHOT提供语句级的一致性,SNAPSHOT事务隔离级别提供事务级的一致性。

五、全部6种隔离级别的加锁模式:

开始说过事务隔离级别主要就是控制读操作加什么锁,锁占用多长时间的的,因此只有搞清各事务隔离级别下的加锁机制才能彻底搞清事务隔离级别的概念和他们的不同。

1.未提交读

select不对读取的数据加锁,会有脏读出现,相当于为select语句添加了with nolock选项。DML语句正常加锁。

2.已提交读

select语句对读取的数据正常加锁,但是不等事务结束才释放锁,而是读完一个页就会释放,会出现不可重复读和幻读。DML语句正常加锁。

3.已提交读快照

SQL Server特有的隔离级别,主要是为了匹配Oracle的已提交读实现的功能,在此隔离级别下,select只会对表加一个Sch-S锁,因此select不会引发在阻塞,但是会加大tempdb的使用量。DML正常加锁。

4.快照

同上,select也只加Sch-S锁,唯一区别在于实现的一致性读是事务级别的,即快照在tempdb中保留的时间更长。DML正常加锁。

这里猜测快照读隔离级别下会在事务第一次select时在tempdb中建立一个完整的快照,而不是仅依赖于MVCC的行版本机制。

5.可重复读

可重复读加的锁与已提交读完全一致,区别在于只有在整个事务完成后才会释放锁,而不是读完一个页就释放,此种加锁方式也避免了不可重复读,因为事务期间其他DML无法获取资源上的锁。

此隔离级别下可以避免不可重复读,但是不可避免幻读。DML正常加锁。

6.序列化读

序列化读加的锁与已提交读有区别,此隔离级别下select操作对索引键加的是键范围锁,而不是普通的S、U、X、IS、IU、IX等。

键范围锁的机制基本与Mysql中的范围锁相似,主要是为了防止幻读,其机制在于select操作不但会将读到的键值锁定,还会将上下键值的范围也锁定。

举例如下:

有主键为1,5,8,9,10的记录,select ... where col between 3 and 7;会使用键范围锁将5这条记录锁定,除此之外还会用一个键范围锁将346这几个虚幻的记录也锁定,这样就不能在读取操作期间插入数据了,可以防止幻读。

Ps:对于序列化加的键范围锁是否是我上边所说的那么精确,还需要具体实验,这里只是根据官网猜测会使用多余的一个键范围锁锁定可能造成幻读的记录(总的键范围锁数目为n+1个,n为满足查询条件的行数),具体实验方法参见我的另一篇博客,有兴趣的可以试试。

http://www.cnblogs.com/leohahah/p/7059852.html

总结:

可以看到SQL Server通过MVCC多版本控制机制在3、4两种隔离级别下实现select语句的不加锁读取,避免了阻塞。在已提交读快照隔离级别下通过mvcc实现了select不阻塞,但是依然会有不可重复读和幻读现象。在快照隔离级别下通过mvcc实现了select不阻塞,同时由于是事务级的快照,也顺带避免了不可重复读和幻读。因此可以发现幻读的解决方式目前只有两种:1是键范围锁,2是mvcc机制的事务级镜像(即snapshot隔离级别的方式)。

Ps:关于Mvcc机制的实现方式参考https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/snapshot-isolation-in-sql-server,但是此文中关于snapshot和read_committed_snapshot的解释有些矛盾,文中把这两种隔离级别混淆了,但是事实上通过试验可以看到这两种isolation level的差别与Mysql中RR和RC下一致性读的差别是很相似的。正如我之前所写的snapshot实现的是事务级的一致性,而read_committed_snapshot实现的是语句级的一致性。

参考文档:

https://docs.microsoft.com/zh-cn/sql/t-sql/statements/set-transaction-isolation-level-transact-sql

https://msdn.microsoft.com/zh-cn/library/jj856598(v=sql.120).aspx

SQL Server 事务隔离级别的更多相关文章

  1. SQL Server 事务隔离级别详解

    标签: SQL SEERVER/MSSQL SERVER/SQL/事务隔离级别选项/设置数据库事务级别 SQL 事务隔离级别 概述 隔离级别用于决定如果控制并发用户如何读写数据的操作,同时对性能也有一 ...

  2. 【转】SQL Server 事务隔离级别详解

    SQL 事务隔离级别 概述 隔离级别用于决定如果控制并发用户如何读写数据的操作,同时对性能也有一定的影响作用. 步骤 事务隔离级别通过影响读操作来间接地影响写操作:可以在回话级别上设置事务隔离级别也可 ...

  3. SQL Server事务隔离级别

    事务 定义 事务是作为单个逻辑工作单元执行的一系列操作. 一个逻辑工作单元必须有四个属性,称为原子性.一致性.隔离性和持久性 (ACID) 属性,只有这样才能成为一个事务. 一个事务可以包含多个操作. ...

  4. 理解Sql Server 事务隔离层级(Transaction Isolation Level)

    关于Sql Server 事务隔离级别,百度百科是这样描述的 隔离级别:一个事务必须与由其他事务进行的资源或数据更改相隔离的程度.隔离级别从允许的并发副作用(例如,脏读或虚拟读取)的角度进行描述. 隔 ...

  5. Transcation And Lock--SQL SERVER 事务隔离级别

    SQL SERVER 事务隔离级别:1.未提交读(READ UNCOMMITED)    允许脏读,读取数据时不加共享锁,与使用WITH(NOLOCK)结果相同2.已提交读    不允许脏读,读取数据 ...

  6. sql server 事务隔离性 snapshot 、read committed说明

    一. --该 read committed 默认事务隔离级别 在 systemuser修改事务未完成时 select * from [SystemUser] where id=62; 该语句是不可读取 ...

  7. sql设置事务隔离级别

    SET TRANSACTION一共有以下几种级别: SET TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED | READ COMMITTED | REPE ...

  8. oracle,mysql,sql server三大数据库的事务隔离级别查看方法

    1:mysql的事务隔离级别查看方法 mysql 最简单,执行这条语句就行:select @@tx_isolation  详情: 1.查看当前会话隔离级别 select @@tx_isolation; ...

  9. Sql Server 事务隔离级别的查看及更改

    根据自身 Sql Server 的情况来自定义 事务隔离级别,将会更加的满足需求,或提升性能.例如,对于逻辑简单的 Sql Server,完全可以使用 read uncommitted 模式,来减少死 ...

随机推荐

  1. 【转载】LINUX 和 WINDOWS 内核的区别

    LINUX 和 WINDOWS 内核的区别 [声明:欢迎转载,转载请注明出自CU ACCESSORY http://linux.chinaunix.net/bbs/thread-1153868-1-1 ...

  2. 【mac】ansible安装及基础使用

    安装 环境释放 mac 10.12.5 #more /System/Library/CoreServices/SystemVersion.plist 安装命令 #ruby -e "$(cur ...

  3. Python函数属性和PyCodeObject

    函数属性 python中的函数是一种对象,它有属于对象的属性.除此之外,函数还可以自定义自己的属性.注意,属性是和对象相关的,和作用域无关. 自定义属性 自定义函数自己的属性方式很简单.假设函数名称为 ...

  4. meterpreter持久后门

    meterpreter持久后门 背景:meterpreter好是好,但有个大问题,只要目标和本机连接断开一次后就再也连不上了,需要再次用exploit打才行!!! 解决方案:在与目标设备的meterp ...

  5. [转]angular官网 及 Ant Design of Angular

    https://angular.io/cli https://www.angular.cn/guide/quickstart https://ng.ant.design/docs/introduce/ ...

  6. EF怎样实现ORM思想的(转载)

    EF简介 实体框架(Entity Framework)简称EF,是微软以ADO.NET为基础所发展出来的对象关系对应(O/R Mapping)解决方案.是ADO.NET中的一组支持开发面向数据的软件应 ...

  7. 44.Linux君正X1000-添加st7789v显示

    由于板子LCD旧屏是ili9335型号的,旧屏有时候会断货,如果断货则使用一个st7789v型号的LCD 它们两个屏的区别在于初始化屏的参数不同,引脚都一样,也就是说需要使板子同时支持ili9335型 ...

  8. mybatis_13一级缓存

    1. Mybatis的缓存理解 Mybatis的缓存,包括一级缓存和二级缓存,一级缓存是默认使用的.二级缓存需要手动开启. 一级缓存指的就是sqlsession,在sqlsession中有一个数据区域 ...

  9. (6)Jquery1.8.3快速入门_过滤选择器

    一.Jquery的基本过滤选择器: 基本过滤选择器: 1. :first 选取第一个元素 2. :last 选取最后一个元素 3.:not(selector) 去除所有与给定的选择器匹配的元素 4.: ...

  10. 史上最全python面试题详解 (二)(附带详细答案(关注、持续更新))

    23.re的match和search区别? re.match()从开头开始匹配string. re.search()从anywhere 来匹配string. # 多行模式>>> re ...