SQL Server事务的隔离级别
作者:网络转载 发布时间:[ 2016/10/12 10:45:24 ] 推荐标签:数据库 SQL Server
使用快照隔离级别
快照隔离级别需要在数据库中一次性地激活。激活之后,每个连接可以在需要的时候使用它。
USE master;
ALTER DATABASE AdventureWorks
SET ALLOW_SNAPSHOT_ISOLATION ON;
现在假设我们希望运行一些基于Sales.SalesOrderDetail表的报表,但需要一致性的读操作。执行以下语句为事务激活快照隔离级别并开始一个返回订单行合计的事务。记住OrderTotal的值。
USE AdventureWorks;
SET TRANSACTION ISOLATION LEVEL SNAPSHOT
BEGIN TRAN
SELECT SUM(LineTotal) as OrderTotal
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 43659
参数SNAPSHOT的含义:
1. 指定事务中任何语句读取的数据都将是在事务开始时便存在的数据的事务上一致的版本。事务只能识别在其开始之前提交的数据修改。在当前事务中执行的语句将看不到在当前事务开始以后由其他事务所做的数据修改。其效果好像事务中的语句获得了已提交数据的快照,因为该数据在事务开始时存在。
2. 除非正在恢复数据库,否则 SNAPSHOT 事务不会在读取数据时请求锁。读取数据的 SNAPSHOT 事务不会阻止其他事务写入数据。写入数据的事务也不会阻止 SNAPSHOT 事务读取数据。
3. 在数据库恢复的回滚阶段,如果尝试读取由其他正在回滚的事务锁定的数据,则 SNAPSHOT 事务将请求一个锁。在事务完成回滚之前,SNAPSHOT 事务会一直被阻塞。当事务取得授权之后,便会立即释放锁。
4. 必须将 ALLOW_SNAPSHOT_ISOLATION 数据库选项设置为 ON,才能开始一个使用 SNAPSHOT 隔离级别的事务。如果使用 SNAPSHOT 隔离级别的事务访问多个数据库中的数据,则必须在每个数据库中将 ALLOW_SNAPSHOT_ISOLATION 都设置为 ON。
5. 不能将通过其他隔离级别开始的事务设置为 SNAPSHOT 隔离级别,否则将导致事务中止。如果一个事务在 SNAPSHOT 隔离级别开始,则可以将它更改为另一个隔离级别,然后再返回 SNAPSHOT。一个事务从执行 BEGIN TRANSACTION 语句开始。
6. 在 SNAPSHOT 隔离级别下运行的事务可以查看由该事务所做的更改。例如,如果事务对表执行 UPDATE,然后对同一个表发出 SELECT 语句,则修改后的数据将包含在结果集中。
打开第二个查询窗口并更新SalesOrderDetail表以更改查询窗口1中用到的基础数据。(如果希望重复这个示例,将OrderQty的值5更改为其他数字以使以下代码能真正地更改数据库中的数据):
USE AdventureWorks;
UPDATE Sales.SalesOrderDetail
SET OrderQty = 5
WHERE SalesOrderID = 43659
AND ProductID = 777
关闭查询窗口2,切换到查询窗口1,然后重复下面的SELECT语句。
SELECT SUM(LineTotal) as OrderTotal
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 43659
可以看出,由于快照隔离级别忽略了事务运行过程中数据的更改,因此结果与以前的相同。在快照级别下总会提供在事务开始时后提交的值。
提交这个事务并执行以下代码再次重复这个查询:现在可看到,由于事务结束了,因此结果发生了变化。
COMMIT TRAN
SELECT SUM(LineTotal) as OrderTotal
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 43659
执行以下代码关闭AdventureWorks数据库的快照隔离级别:
ALTER DATABASE AdventureWorks
SET ALLOW_SNAPSHOT_ISOLATION OFF;
避免同时发生的数据更新
如前所述,快照隔离级别并不在读操作的时候锁定数据,但能够在整个事务中提供一致性的视图。在某些情况下,有必要在整个事务的执行过程中锁定数据以避免其他事务对数据的更改。假设希望为一个订单开发票。首先需要获取数据并检查它,然后为其生成发票。在这种情况下,需要从事务起始锁定数据以避免其他事务更改它。在这种情况下,快照隔离或者已提交读隔离级别都不是好的选择。对于这种情况,可以使用可重复读隔离级别。这个隔离级别与没有快照的已提交读级别的工作过程相似,但它会保持共享锁直至事务结束。因此,它防止了对数据的更新。
使用可重复读隔离级别
假设希望处理OrderID为43659的订单。首先,必须选择数据。为了防止其他事务更改正在读的数据,使用可重复读隔离。
USE AdventureWorks;
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ
BEGIN TRAN
SELECT SalesOrderID, SalesOrderDetailID, ProductID, OrderQty
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 43659
参数REPEATABLE READ的含义:
1. 指定语句不能读取已由其他事务修改但尚未提交的行,并且指定,其他任何事务都不能在当前事务完成之前修改由当前事务读取的数据。
2. 对事务中的每个语句所读取的全部数据都设置了共享锁,并且该共享锁一直保持到事务完成为止。这样可以防止其他事务修改当前事务读取的任何行。其他事务可以插入与当前事务所发出语句的搜索条件相匹配的新行。如果当前事务随后重试执行该语句,它会检索新行,从而产生幻读。由于共享锁一直保持到事务结束,而不是在每个语句结束时释放,所以并发级别低于默认的 READ COMMITTED 隔离级别。此选项只在必要时使用。
打开第二个查询窗口并执行以下代码尝试更新SalesOrderDetail表以更改查询窗口1中要使用的基础数据:
UPDATE Sales.SalesOrderDetail
SET OrderQty = 5
WHERE SalesOrderID = 43659
AND ProductID = 777
查询会等待。不像快照隔离级别,不可能更新数据,因为共享锁会保持以防止其他事务更改数据。这个锁可以通过前面用过的管理视图sys.dm_tran_locks查看。
单击工具条上的"取消执行查询"按钮取消在查询窗口2中的查询。而执行以下INSERT语句在订单中加入一个新行项。
INSERT INTO Sales.SalesOrderDetail
(
SalesOrderID,
CarrierTrackingNumber,
OrderQty,
ProductID,
SpecialOfferID,
UnitPrice,
UnitPriceDiscount
)
VALUES(43659,'4911-403C-98',1,758,1,874,0)
注意,即使正处于可重复读隔离级别,这个语句也会成功执行。因为可重复读会锁定数据以阻止数据的更新,但INSERT语句向数据库中插入新数据,这是允许的。新行处于查询窗口1中事务SELECT语句的查询范围之中,所以会在事务下一次获取相同数据的时候被读取到。这称作幻像读。
重复SELECT语句并提交这个事务,如下所示:
SELECT SalesOrderID, SalesOrderDetailID, ProductID, OrderQty
FROM Sales.SalesOrderDetail
WHERE SalesOrderID = 43659
COMMIT TRAN
可以观察到,新行被SELECT语句读取了,因为它处于这个语句的查询范围之内。可重复读级别会阻止现有数据被更改,但不会阻止新数据插入SELECT语句的查询范围内。
其他
SET TRANSACTION一共有以下几种级别:
SET TRANSACTION ISOLATION LEVEL
{ READ UNCOMMITTED
| READ COMMITTED
| REPEATABLE READ
| SNAPSHOT
| SERIALIZABLE
}
[ ; ]
上面的例子中没有提到的几种隔离级别的说明:
1.READ UNCOMMITTED
指定语句可以读取已由其他事务修改但尚未提交的行。
在 READ UNCOMMITTED 级别运行的事务,不会发出共享锁来防止其他事务修改当前事务读取的数据。READ UNCOMMITTED 事务也不会被排他锁阻塞,排他锁会禁止当前事务读取其他事务已修改但尚未提交的行。设置此选项之后,可以读取未提交的修改,这种读取称为脏读。在事务结束之前,可以更改数据中的值,行也可以出现在数据集中或从数据集中消失。该选项的作用与在事务内所有 SELECT 语句中的所有表上设置 NOLOCK 相同。这是隔离级别中限制少的级别。
在 SQL Server 2005 中,您还可以使用下列任意一种方法,在保护事务不脏读未提交的数据修改的同时尽量减少锁定争用:
1. READ COMMITTED 隔离级别,并将 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON。
2. SNAPSHOT 隔离级别。
1.READ COMMITTED
指定语句不能读取已由其他事务修改但尚未提交的数据。这样可以避免脏读。其他事务可以在当前事务的各个语句之间更改数据,从而产生不可重复读取和幻像数据。该选项是 SQL Server 的默认设置。
READ COMMITTED 的行为取决于 READ_COMMITTED_SNAPSHOT 数据库选项的设置:
1. 如果将 READ_COMMITTED_SNAPSHOT 设置为 OFF(默认设置),则数据库引擎 会使用共享锁防止其他事务在当前事务执行读取操作期间修改行。共享锁还会阻止语句在其他事务完成之前读取由这些事务修改的行。语句完成后便会释放共享锁。
2. 如果将 READ_COMMITTED_SNAPSHOT 设置为 ON,则数据库引擎 会使用行版本控制为每个语句提供一个在事务上一致的数据快照,因为该数据在语句开始时存在。不使用锁来防止其他事务更新数据。
当 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON 时,您可以使用 READCOMMITTEDLOCK 表提示为 READ_COMMITTED 隔离级别上运行的事务中的各语句请求共享锁,而不是行版本控制。
注意:设置 READ_COMMITTED_SNAPSHOT 选项时,数据库中仅允许存在执行 ALTER DATABASE 命令的连接。在 ALTER DATABASE 完成之前,数据库中不允许有其他打开的连接。数据库不必处于单用户模式。
1.SERIALIZABLE
请指定下列内容:
1. 语句不能读取已由其他事务修改但尚未提交的数据。
2. 任何其他事务都不能在当前事务完成之前修改由当前事务读取的数据。
3. 在当前事务完成之前,其他事务不能使用当前事务中任何语句读取的键值插入新行。
范围锁处于与事务中执行的每个语句的搜索条件相匹配的键值范围之内。这样可以阻止其他事务更新或插入任何行,从而限定当前事务所执行的任何语句。这意味着如果再次执行事务中的任何语句,则这些语句便会读取同一组行。在事务完成之前将一直保持范围锁。这是限制多的隔离级别,因为它锁定了键的整个范围,并在事务完成之前一直保持范围锁。因为并发级别较低,所以应只在必要时才使用该选项。该选项的作用与在事务内所有 SELECT 语句中的所有表上设置 HOLDLOCK 相同。
需要注意的地方:
1. 一次只能设置一个隔离级别选项,而且设置的选项将一直对那个连接始终有效,直到显式更改该选项为止。事务中执行的所有读取操作都会在指定的隔离级别的规则下运行,除非语句的 FROM 子句中的表提示为表指定了其他锁定行为或版本控制行为。
2. 事务隔离级别定义了可为读取操作获取的锁类型。针对 READ COMMITTED 或 REPEATABLE READ 获取的共享锁通常为行锁,尽管当读取引用了页或表中大量的行时,行锁可以升级为页锁或表锁。如果某行在被读取之后由事务进行了修改,则该事务会获取一个用于保护该行的排他锁,并且该排他锁在事务完成之前将一直保持。例如,如果 REPEATABLE READ 事务具有用于某行的共享锁,并且该事务随后修改了该行,则共享行锁便会转换为排他行锁。
3. 在事务进行期间,可以随时将事务从一个隔离级别切换到另一个隔离级别,但有一种情况例外。即在从任一隔离级别更改到 SNAPSHOT 隔离时,不能进行上述操作。否则会导致事务失败并回滚。但是,可以将在 SNAPSHOT 隔离中启动的事务更改为任何其他隔离级别。
4. 将事务从一个隔离级别更改为另一个隔离级别之后,便会根据新级别的规则对更改后读取的资源执行保护。在更改前读取的资源将继续按照以前级别的规则受到保护。例如,如果某个事务从 READ COMMITTED 更改为 SERIALIZABLE,则在该事务结束前,更改后所获取的共享锁将一直处于保留状态。
5. 如果在存储过程或触发器中发出 SET TRANSACTION ISOLATION LEVEL,则当对象返回控制时,隔离级别会重设为在调用对象时有效的级别。例如,如果在批处理中设置 REPEATABLE READ,并且该批处理调用一个将隔离级别设置为 SERIALIZABLE 的存储过程,则当该存储过程将控制返回给该批处理时,隔离级别会恢复为 REPEATABLE READ。
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系SPASVO小编(021-61079698-8054),我们将立即处理,马上删除。
相关推荐
远程连接sql server 2000服务器的解决方案SQL Server修改数据库物理文件存在位置探讨SQL Server并发处理队列数据不阻塞解决方案迁移SQL Server到Azure SQL实战SQL SERVER 的前世今生?各版本功能对比SQL Server的WITH (NOLOCK)SQL Server如何用触发器捕获DML操作的会话信息SQL Server里书签查找的性能伤害监控SQL Server事务复制SQL Server数据库镜像下有效的索引维护SQL Server不停机移动镜像数据库SQL Server数据库备份压缩拷贝实例SQL Server数据库优化SQL SERVER的统计信息SQL Server里如何处理死锁SQL SERVER批量生成编号
更新发布
功能测试和接口测试的区别
2023/3/23 14:23:39如何写好测试用例文档
2023/3/22 16:17:39常用的选择回归测试的方式有哪些?
2022/6/14 16:14:27测试流程中需要重点把关几个过程?
2021/10/18 15:37:44性能测试的七种方法
2021/9/17 15:19:29全链路压测优化思路
2021/9/14 15:42:25性能测试流程浅谈
2021/5/28 17:25:47常见的APP性能测试指标
2021/5/8 17:01:11热门文章
常见的移动App Bug??崩溃的测试用例设计如何用Jmeter做压力测试QC使用说明APP压力测试入门教程移动app测试中的主要问题jenkins+testng+ant+webdriver持续集成测试使用JMeter进行HTTP负载测试Selenium 2.0 WebDriver 使用指南