代码改变世界

SQL Server 之 事务与隔离级别实例讲解

2018-07-06 14:31  Tony、  阅读(513)  评论(0编辑  收藏  举报

SQL Server 之 事务与隔离级别实例讲解

  SQL Server 实现了6个隔离级别来防止并发情况下,类似企图并发的访问或修改同一数据时问题的发生。本文将带你体验全部6个隔离级别。正如你接下来将看到的,你将理解每个隔离级别所能达成的效果以及何时使用它。

一、事务简介

  SQL Server的6个隔离级别中有5个是用于隔离事务的,它们因而被称作事务隔离级别。另外的一个工作于语句级别。

  在现实中要求多个数据修改操作必须要么完全成功要么什么也没发生的例子。当数据被合并到数据库时,可能有多个表需要更新。当顾客下订单时,Order表、Invoice Line Item表和Product表的数据可能都需要更新。购买机票也许要求更新Passenger表和Reservations表。无论何时当一个操作要求多个数据更改操作整体地作为单一的单元来处理,这就是需要使用事务的时候。

  如果事务中所有的数据更改操作都成功了,那么这些数据更改就可以被提交(也就是持久化到数据库)。否则,截止到失败点事务中所发生的所有数据更改必须被回滚(也就是撤销操作,什么也不曾发生)。
  事务相关操作,参照 http://www.cnblogs.com/xinaixia/p/4831198.html 。

二、 隔离级别简介

  必须小心对待并发情况,因为它们可能引发已知的并发性问题,包括“脏读”、“不可重复读”和“幻像读”,这些问题可能反过来导致数据的不良后果。正如我们已经知道的,为了防止并发性问题,隔离级别用于将事务或语句相互间隔离开来。

  下面是SQL Server 2008中定义的隔离级别名称:

  1、Transaction Isolation Level

  [1] READ UNCOMMITTED  (未提交读,读脏),相当于(NOLOCK)
  [2] READ COMMITTED (Default)  (已提交读,默认级别)
  [3] REPEATABLE READ  (可以重复读),相当于(HOLDLOCK)
  [4] SERIALIZABLE  (可序列化)
  [5] SNAPSHOT  (快照)

  2、Statement Isolation Level

  [6] READ COMMITTED SNAPSHOT  (已经提交读隔离)


  对于前四个隔离级别:READ UNCOMMITTED < READ COMMITTED < REPEATABLE READ < SERIALIZABLE
  隔离级别越高,读操作的请求锁定就越严格,锁的持有时间久越长;所以隔离级别越高,一致性就越高,并发性就越低,同时性能也相对影响越大。

  获取事务隔离级别(isolation level)

DBCC USEROPTIONS 

  设置隔离

设置会话隔离
SET TRANSACTION ISOLATION LEVEL <ISOLATION NAME>
--注意:在设置回话隔离时(REPEATABLE READ)两个单词需要用空格间隔开,但是在表隔离中可以粘在一起(REPEATABLEREAD)

设置查询表隔离
SELECT ....FROM <TABLE> WITH (<ISOLATION NAME>) 

  正如你在下面的例子中即将看到的,隔离级别越高,提供的保护级别也越高(防止更多的并发性问题)。并且,每个隔离级别包括了前一个级别所提供的保护,因此,每个后续的更高隔离级别以避免更多并发性问题的形式提供了额外的保护。但是,世上没有免费的午餐,隔离级别越高,数据可用性就越低。选择合适的隔离级别是一种在高度安全的并发性和数据的高可用性之间寻求平衡的行为。

三、引入实例

  为了创建并发环境,所有例子使用2个SQL Server Session,每个会话运行一个不同的事务,每个事务访问相同的资源。在SQL Server Management Studio中,每个查询窗口代表了一个不同的Session,因此,你可以在SQL Server Management Studio中为不同的事务使用不同的查询窗口。
  所有例子包含了真实场景以便你将这一切建立在现实的基础上。

  1、READ UNCOMMITTED 未提交读

  READ UNCOMMITTED 事务隔离级别根本就没有提供事务间的隔离,它允许违反并发性原则的最基本形式之一 -- 脏读。当一个事务能够读取另一个事务中已经Update但尚未Commit的数据时,“脏读”就发生了。READ UNCOMMITTED 读操作不申请锁,运行读取未提交的修改,也就是允许读脏数据,读操作不会影响写操作请求排他锁。

  READ UNCOMMITTED 常应用于:单用户系统;系统中两个事务同时访问同一资源的可能性为零或几乎为零;当使用Rowversion数据类型控制并发性时 。

  

   2、READ COMMITTED 已提交读,默认

  通过仅允许一个事务读取另一个事务中已经提交的数据,READ COMMITTED 事务隔离级别防止了“脏读”问题。这是SQL Server中默认的事务隔离级别。

  它是SQL SERVER默认的隔离级别,可以避免读取未提交的数据,隔离级别比READ UNCOMMITTED未提交读的级别更高;该隔离级别读操作之前首先申请并获得共享锁,允许其他读操作读取该锁定的数据,但是写操作必须等待锁释放,一般读操作读取完就会立刻释放共享锁。
  

   3、REPEATABLE READ 可重复读

  正如你在前一个事务隔离级别的步骤2所看到的,Session 2中的事务能够修改已经被Session 1中的事务读取的数据。正像真实场景中所描述的,这可能导致“LOST UPDATE”。REPEATABLE READ 事务隔离级别不允许这种情况发生,因为它违背了REPEATABLE READ原则。换句话说,Session 1中的事务读取同一数据可能会产生不同的结果。

  该级别保证在一个事务中的两个读操作之间,其他的事务不能修改当前事务读取的数据,该级别事务获取数据前必须先获得共享锁同时获得的共享锁不立即释放一直保持共享锁至事务完成,所以此隔离级别查询完并提交事务很重要。
  

   4、SERIALIZABLE 可序列化

  对于前面的REPEATABLE READ能保证事务可重复读,但是事务只锁定查询第一次运行时获取的数据资源(数据行),而不能锁定查询结果之外的行,就是原本不存在于数据表中的数据。因此在一个事务中当第一个查询和第二个查询过程之间,有其他事务执行插入操作且插入数据满足第一次查询读取过滤的条件时,那么在第二次查询的结果中就会存在这些新插入的数据,使两次查询结果不一致,这种读操作称之为幻读。
  为了避免幻读需要将隔离级别设置为 SERIALIZABLE 。为了向你展示SERIALIZABLE 事务隔离级别防止的并发性问题,本例我们从REPEATABLE READ 事务隔离级别开始。
  

  SNAPSHOT 快照分为 SNAPSHOT和READ COMMITTED SNAPSHOT两种隔离(可以把事务已经提交的行的上一版本保存在TEMPDB数据库中):
  [1] SNAPSHOT隔离级别在逻辑上与SERIALIZABLE类似;
  [2] READ COMMITTED SNAPSHOT隔离级别在逻辑上与 READ COMMITTED类似;
  不过在快照隔离级别下读操作不需要申请获得共享锁,所以即便是数据已经存在排他锁也不影响读操作。而且仍然可以得到和SERIALIZABLE与READ COMMITTED隔离级别类似的一致性;如果目前版本与预期的版本不一致,读操作可以从TEMPDB中获取预期的版本。

  如果启用任何一种基于快照的隔离级别,DELETE和UPDATE语句在做出修改前都会把行的当前版本复制到TEMPDB中,而INSERT语句不需要在TEMPDB中进行版本控制,因为此时还没有行的旧数据。

  无论启用哪种基于快照的隔离级别都会对更新和删除操作产生性能的负面影响,但是有利于提高读操作的性能因为读操作不需要获取共享锁。

   5、SNAPSHOT 快照 

  也许你已经注意到,在上述例1到例4中,防止并发性问题的同时也降低了数据的可访问性。先是不允许Read,然后是不允许Update,不允许Insert。SNAPSHOT事务隔离级别防止了之前那些隔离级别所能防止的许多并发性问题,同时降低了与之相关的成本。它允许更高的数据可用性。
  通过在事务开始前在TempDB中使用row versions创建一份数据库的虚拟快照,SNAPSHOT事务隔离级别完成了此壮举。此后它只允许事务访问该数据库虚拟快照。这种方法被称做“基于版本控制的隔离”(versioning-based isolation)。
  使用versioning-based isolation,事务仅能看到虚拟快照中的数据。因此,其他事务仍然能够访问同一数据,只要它们不去修改已经被第一个事务修改过的数据就好。如果那样做了(企图修改数据),那么,那些事务将会被回滚并以错误消息终止。
  只有当数据库中启用SNAPSHOT事务隔离级别的开关打开后,才能使用它。打开此开关将告知数据库去设置版本化环境。理解这一点很重要,因为,一旦版本化开启,数据库会有维护版本化的开销,无论是否有事务正在使用SNAPSHOT事务隔离级别。

  在SNAPSHOT隔离级别下,当读取数据时可以保证操作读取的行是事务开始时可用的最后提交版本。同时SNAPSHOT隔离级别也满足前面的已提交读,可重复读,不幻读;该隔离级别实用的不是共享锁,而是行版本控制,使用SNAPSHOT隔离级别首先需要在数据库级别上设置相关选项。
  

   6、READ COMMITTED SNAPSHOT 提交读快照

  到目前为止,所有的隔离级别都是将事务相互间隔离开来。一旦初始事务完成了,对其他事务变得不可用的资源才又变得可用。READ COMMITTED SNAPSHOT 隔离级别在这点上有所不同,它能够读取其已经被他事务提交的数据。它也是通过数据库开关来打开的。然后,任何使用READ COMMITTED SNAPSHOT 隔离级别的事务将通过版本化起作用。

  READ COMMITTED SNAPSHOT也是基于行版本控制,但是READ COMMITTED SNAPSHOT的隔离级别是“ 读操作之前的最后已提交版本,而不是事务前的已提交版本 ”,有点类似前面的READ COMMITTED能保证已提交读,但是不能保证可重复读,不能避免幻读,但是又比 READ COMMITTED隔离级别多出了不需要获取共享锁就可以读取数据。
  要启用READ COMMITTED SNAPSHOT隔离级别同样需要修改数据库选项。
  

四、小结

隔离级别   解决的并发性问题 存在的并发性问题
READ UNCOMMITTED 不适用于并发场合 Dirty Reads, Non-repeatable Reads, Phantom Reads
READ COMMITTED Dirty Reads Lost Update , Non-repeatable Reads, Phantom Reads
REPEATABLE READ Non-repeatable Reads Phantom Reads, potentially Deadlocking
SERIALIZABLE Phantom Reads Less Data Availability, potentially Deadlocking
SNAPSHOT 上述所有并发性问题 事务访问的是虚拟快照,其他事务Committed的数据对当前事务仍然不可见,也不允许Update被其他事务Updated的数据。
READ COMMITTED SNAPSHOT 上述所有并发性问题
 
 
提交读:Session1 在事物没有执行结束的时候 Session2可以修改事物范围内的数据。
可重复读:Session1在事物么有执行结束的时候 Session2不可以修改事物范围内的数据
提交度和可重复读都是针对于Session1来设定的 于Session2无关
参考地址:https://www.cnblogs.com/xinaixia/p/5703175.html