翻译:Transaction Isolation Levels

ISO隔离级别

 

下表显示了不同的ISO隔离级别以及它们的并发性副作用:

 

当我们检查这个表时我们可以看到不同的事务隔离级别被设计为消除并发性影响

SQL Server 2005增加了两个额外的事务隔离级别这两个级别都处理使用快照的问题:

 

READ_COMMITTED_SNAPSHOT是数据库级别的设置如果打开它并提交事务隔离级别那么它将使用行版本控制在语句开始时显示数据的事务一致视图

快照隔离级别还利用行版本控制在语句开始时呈现数据的事务一致性视图这要求打开ALLOW_SNAPSHOT_ISOLATION数据库设置并让查询发出SET事务隔离级别快照语句

在这两个快照隔离级别中结果是读者不会阻止作者作者不会阻止读者此外读者将不能阅读任何飞行数据修改从其他事务

正如我已经提到的这两种方法都使用行版本控制在使用行版本控制时,SQL Server中的数据库引擎将维护受事务影响的行的版本利用row-versioning:

1. 消除读取事务上的共享锁

2. 减少阻塞(读取事务)。

3. 增加数据修改所需的资源

4. 增加tempdb中的活动(其中存储行版本控制信息)。

        a.所有数据库数据修改都有行版本控制

5. 每个数据记录都有一个14字节的记录后缀

 

并发性的影响

上面的图表提到了几种不同的并发副作用让我们来解释一下它们这些效应在BOL中定义为并发效应:

 

脏读”(ISO中称为不提交依赖”)发生在第二个事务选择正在被另一个事务更新的行时当修改后的数据在另一个事务提交正在修改数据的事务之前被读取时会发生脏读如果要回滚该事务则第二个事务只返回与数据库中不存在的数据的行通过防止读取正在更改的数据可以避免这种影响

当事务多次读取同一行时就会发生不可重复读取(ISO中称为不一致分析”),并且不同的读取之间的结果是不同的当另一个事务修改并将更改提交给行时就会发生这种情况与脏读类似不同之处在于在不可重复读中写事务已经成功提交了事务而在脏读中写事务被回滚可以通过防止数据更改直到完成数据读取从而避免这种影响

当读取数据的事务正在读取数据范围而另一个事务插入或删除一行时就会发生虚读如果再次发出读取事务发出的语句将返回额外的行(对于insert事务),或者返回更少的行(对于delete事务)。通过防止事务在读取数据时插入或删除数据可以避免这种影响

 

缺失/双读发生在:

² 读取事务在索引扫描操作中读取一系列行在读取行期间由第二个事务更新从而更改索引键列从而更改其在扫描中的位置如果更新将一行从扫描结束移动到开始则读取事务可能会错过该行;相反如果更新将行从扫描的开始移动到末尾那么该行可以被读取两次

² 如果读未提交隔离级别中的读取事务正在执行分配顺序扫描(使用IAM页面),而另一个事务导致页面分割则读取事务可能会错过正在读取的行

当您阅读这些效果时您应该能够看到当您在努力防止任何这些并发效果时您正在数据库中创建更多的锁定(从而可能产生更多的阻塞)。

 

并发效果的示例

让我们运行一些示例看看这些不同的并发效果如何在不同的事务隔离级别中表现出来所有这些例子都使用了两个查询窗口;一个运行读事务另一个运行写事务查询利用“WAITFOR DELAY”给您一点时间启动一个事务并切换到另一个查询窗口运行另一个查询窗口

首先是数据库初始化代码在运行每个测试之前需要运行此代码它被放入一个存储过程中以便在必要时可以轻松运行

"未提交"

Read Uncommitted隔离级别中我们将研究如何允许脏读这将通过在一个执行更新的查询窗口中启动事务来实现同时在第二个查询窗口中read uncommitted事务隔离级别中运行select语句以便查询读取被修改的数据一段时间后第一个查询窗口中的事务将回滚您将看到第二个查询窗口返回了从未提交到表的数据

1. 在第一个查询窗口中运行以下语句:

2. 在第二个查询窗口中运行以下语句:

3. 从结果中可以看到第二个查询立即返回它返回随后在第一个查询窗口中回滚的值

 

 "已提交"

"已提交"读的测试中我们将重新运行这些语句第二个查询窗口设置为使用已读提交事务隔离级别因此在第二个查询窗口中运行的select语句必须等到第一个事务完成(事务被提交或回滚)之后才能读取数据——它被打开的事务阻塞 。

   1.在第一个查询窗口中运行以下语句:

   2.在第二个查询窗口中运行以下语句:

   3.如您所见查询窗口2中的语句必须等待查询窗口1中的事务完成后才能运行查询窗口2在查询窗口1完成后返回表中的值

 

可重复读

对于下一个可重复读取的隔离级别我们将展示在这个隔离级别上的事务如何返回相同的数据,该事务正在从表中读取数据两次每次读取间隔一段时间在这个隔离级别下它必须为被读取的行读取完全相同的数据,因此它将阻塞第二个试图更新其中一些行的事务然后我们将从可重复读取更改为已提交读取以显示允许更新运行的效果

    1.在第一个查询窗口中运行以下语句:

    2.在第二个查询窗口中运行以下语句

    3.注意查询窗口2一直等到查询窗口1完成因为查询窗口1是可重复读取的

    4.重新运行步骤1 - 3:

     更改查询窗口1以使用已提交的读取隔离级别并运行代码

     运行查询窗口2中的代码

    5.注意查询窗口2立即完成在查询窗口1中第二个select语句从第一个select语句返回不同的结果

 

可序列化的

在刚刚执行的可重复读测试中我们看到如何防止对数据的更新。serializable隔离级别进一步实现了这一点并防止对该表进行插入或删除为了测试这一点我们将从可重复读取中重新运行测试我们将把隔离级别更改为serializable,并尝试执行插入而不是更新然后我们将在可重复读隔离级别中运行这个测试展示如何允许插入运行

    1.在第一个查询窗口中运行以下语句:

    2.在第二个查询窗口中运行以下语句:

    3.注意查询窗口2中的插入将等待直到查询窗口1中的事务完成

    4.重新运行步骤1 - 3:

         a.更改查询窗口1以使用可重复读取隔离级别并运行代码

         b. 运行查询窗口2中的代码

    5.注意查询窗口2中的insert立即运行查询窗口1中的第二个select语句返回插入的行.

 

快照

我们所研究的读提交/未提交事务级别也存在丢失/双读的问题可重复读/可串行隔离级别消除了这个问题但这样做的代价是严重阻塞其他事务快照隔离级别消除了与serializable隔离级别相同的并发性副作用并且在不引入锁(因此消除了阻塞)的情况下也实现了这一点在这个测试中我们将首先显示快照隔离级别中没有阻塞然后显示在使用serializable隔离级别时如何阻塞这些相同的语句

    1.在第一个查询窗口中运行以下语句:

    2.在第二个查询窗口中运行以下语句:

    3.注意查询窗口2立即完成但是数据修改没有反映在查询窗口1

    4.如果您要更改查询窗口1以使用serializable隔离级别并重新运行测试您将会看到查询窗口2现在将被阻塞并且现在将等到查询窗口1完成后才能插入行

 

NOLOCK查询提示如何适用呢?

表提示NOLOCK(与表提示READUNCOMMITTED相同)与指定设置的事务级别READUNCOMMITTED相同您可以通过运行READ UNCOMMITTED代码看到这一点而对于query window 2,您可以运行以下代码:

 

如果您决定实现快照隔离并且您的当前代码正在使用NOLOCK(READUNCOMMITTED)表提示那么这些指定的提示将具有优先级——您将需要修改代码以获得利用快照隔离级别的好处

 

总结

ISO隔离级别中当我们更改查询运行的隔离级别时我们要么减少锁(但允许读取脏数据),要么增加所涉及的锁以最小化并发性影响快照隔离级别消除了所有并发影响同时在读取事务上保持零阻塞但是由于没有免费的东西因此需要付出代价即增加了tempdb活动增加了用户数据库和tempdb中的存储空间需求尽管如此我觉得如果您使用read uncommitted(nolock),您应该切换到使用已提交的快照隔离级别而不是实现该查询所要实现的no阻塞

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值