`

sql server行版本控制的隔离级别

 
阅读更多

        在SQL Server标准的已提交读(READ COMMITTED)隔离级别下,一个读操作会和一个写操作相互阻塞。未提交读(READ UNCOMMITTED)虽然不会有这种阻塞,但是读操作可能会读到脏数据,这是大部分用户不能接受的。有些关系型数据库(例如Oracle)使用的是另 一种处理方式。在任何一个修改之前,先对修改前的版本做一个复制[WX1] ,后续的一切读操作都会去读这个复制的版本,修改将创建一个新的版本。在这种处理方式下,读、写操作不会相互阻塞。使用这种行版本控制机制的好处,是程序的并发性比较高,但是缺点是用户读到的虽然不是一个脏数据,但是可能是个正在被修改马上就要过期的数据值[WX2] 。如果根据这个过期的值做数据修改,会产生逻辑错误。


 

 [WX1]复制的内容保存在tempdb当中。

 

 [WX2]假如读跟写同时进行,读到的不是现在正被修改的值,如果是读到正被修改的值那就是脏读了。读到的是修改前的值。但是这个值随时会过期。等到修改完就过期了。

 

有些用户可能为了更高的并发性而不在乎这种缺点,所以更喜欢Oracle的那种处理方法。为了满足这部分用户的需求,SQL Server 2005也引入了这种机制,来实现类似的功能。所以选取行版本控制隔离级别也可以成为消除阻塞和死锁的一种手段。

 

SQL Server有两种行版本控制,使用行版本控制的已提交读隔离(READ_COMMITTED_SNAPSHOT)和直接使用SNAPSHOT事务隔离级别。

 

  • READ_COMMITTED_SNAPSHOT数据库选项为ON时,READ_COMMITTED事务通过使用行版本控制提供语句级读取一致性。
  • ALLOW_SNAPSHOT_ISOLATION数据库选项为ON时,SNAPSHOT事务通过使用行版本控制提供事务级读取一致性。

 

下列示例可以说明使用普通已提交读事务,行版本控制的快照隔离事务和行版本控制的已提交读事务的行为差异。

 

实验1:Read Committed Isolation level

 

query1:事务1

 

复制代码
USE AdventureWorks;
GO

--step1:开启第一个事务
BEGIN TRAN tran1
    --step2:执行select操作,查看VacationHours,对查找的记录加S锁
    SELECT EmployeeID, VacationHours
        FROM HumanResources.Employee 
        WHERE EmployeeID = 4;

    --step6: 在第一个事务中重新运行查询语句,发现查询被阻塞
    --这是因为在VacationHours上面有排他锁,现在要查询VacationHours字段又必须获得S锁,但是X锁与S锁冲突
    --所以不能执行查询,被阻塞.
    SELECT EmployeeID, VacationHours
        FROM HumanResources.Employee
        WHERE EmployeeID = 4;

    --step8:因为会话2已经提交了事务,不再阻塞当前查询,因此返回会话2修改好的新数据:40

--step9:回滚或者提交事务
ROLLBACK TRANSACTION;
commit tran tran1
GO
复制代码

 

query2:事务2

 

复制代码
USE AdventureWorks;
GO

--step3:开启第二个事务
BEGIN TRAN tran2;
    --step4:修改VacationHours,需要获得更新锁U,在VacationHours上有S锁,US不冲突,因此可以进行修改.
    --在修改VacationHours以后,更新锁U变成了排他锁X
    UPDATE HumanResources.Employee 
        SET VacationHours = VacationHours - 8  
        WHERE EmployeeID = 4;

    -- step5:在当前事务中查询VacationHours,发现只有40小时
    SELECT VacationHours
        FROM HumanResources.Employee
        WHERE EmployeeID = 4;

--step7:回滚事务
rollback tran tran2
--commit tran tran2
复制代码

 

总结:

 

  1. 事务1中的读操作没有阻塞事务2中的写操作
  2. 事务2中的更新操作阻塞了事务1中后来的读操作,如下图所示:
  3. 事务1两次查询得到的数据分别是48跟40,两次获得的数据内容不一样。所以也成read committed为不可重复读。在read committed隔离级别中,只在语句级别加锁,当语句执行完以后自动释放锁。比如事务1中的第一次查询,虽然查询在事务中进行,并且事务没有提交,但 是此时查询语句执行完以后在table上就找不到锁了。

 

实验2:Snapshot Isolation

 

此示例中,在快照隔离下运行的事务将读取数据,然后由另一事务修改此数据。快照事务不会被其他事务执行的更新操作所阻塞,它忽略数据的修改继续从版本化的行读取数据。也就是说,读取到的是数据修改前的版本。但是,当快照事务尝试修改已由其他事务修改的数据时,它将生成错误并终止。

 

query1:事务1,快照事务

 

复制代码
--实验2:Read Committed Snapshot Isolation level -------------------
USE AdventureWorks;
GO

--step1:启用快照隔离
ALTER DATABASE AdventureWorks
    SET ALLOW_SNAPSHOT_ISOLATION ON;
GO

--step2:设置使用快照隔离级别,前面没有设定是因为数据库默认的隔离界别就是Read Committed
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
GO

--step3:开启第一个事务
BEGIN TRAN tran1
    --step4:执行select操作,查看VacationHours,对查找的记录加S锁
    SELECT EmployeeID, VacationHours
        FROM HumanResources.Employee 
        WHERE EmployeeID = 4;

    --step8:在事务2中修改了数据以后,在事务1中再次运行查询语句
    --此时查询语句没有被阻塞,返回的值是48,也就是事务2修改之前的数据
    --这是因为事务1是从版本化的行读取数据 
    SELECT EmployeeID, VacationHours
        FROM HumanResources.Employee
        WHERE EmployeeID = 4;

    --step10:在事务2提交以后,事务1再次执行查询操作
    --发现查询结果还是48,这是因为事务依然从版本化的行中读取数据
    SELECT EmployeeID, VacationHours
        FROM HumanResources.Employee
        WHERE EmployeeID = 4;


    --step11:在事务2提交修改以后,事务1想再做任何修改时,这里我们修改SickLeaveHours字段的值
    --此时会遇到3960错误,事务1会自动回滚,事务2中的修改不会被回滚.
    UPDATE HumanResources.Employee
        SET SickLeaveHours = SickLeaveHours - 8
        WHERE EmployeeID = 4;

    --附:假如事务2中执行了修改操作,但是没有提交,此时在事务1中执行修改操作会被阻塞
    --此时如果提交事务2中的修改操纵,事务1会遇到3960错误,跟上面一样.
    UPDATE HumanResources.Employee
        SET SickLeaveHours = SickLeaveHours - 8
        WHERE EmployeeID = 4;

rollback tran tran1
commit tran tran1
/*
Msg 3960, Level 16, State 2, Line 1
Snapshot isolation transaction aborted due to update conflict. You cannot use snapshot isolation to
 access table 'HumanResources.Employee' directly or indirectly in database 'AdventureWorks' to update,
  delete, or insert the row that has been modified or deleted by another transaction. Retry the 
  transaction or change the isolation level for the update/delete statement.
*/
--实验2结束------------------------------------
复制代码

 

query2:事务2

 

复制代码
--实验2:Read Committed Snapshot Isolation level -------------------
USE AdventureWorks;
GO

--step5:开启第二个事务
BEGIN TRAN tran2;
    --step6:修改VacationHours,需要获得更新锁U,在VacationHours上有S锁,US不冲突,因此可以进行修改.
    --在修改VacationHours以后,更新锁U变成了排他锁X
    UPDATE HumanResources.Employee 
        SET VacationHours = VacationHours - 8  
        WHERE EmployeeID = 4;

    -- step7:在当前事务中查询VacationHours,发现只有40小时
    SELECT VacationHours
        FROM HumanResources.Employee
        WHERE EmployeeID = 4;

--step9:提交事务2
commit tran tran2
--实验2结束------------------------------------
复制代码

 

总结:

 

  1. 快照事务1的读操作没有阻塞普通事务2的读操作,但是阻塞了事务2的删除操作,如果在事务2中执行delete操作的话会报错:Employees cannot be deleted. They can only be marked as not current.Msg 3609, Level 16, State 1, Line 1The transaction ended in the trigger. The batch has been aborted.
  2. 普通事务2的更新操作,没有阻塞事务1的读操作,但是我们发现事务1中读到数据是事务2更新之前的内容。因为读取的是版本化中的行数据。

 

在上述实验中,我们发现下面两条语句使一起使用的,也就是首先允许数据库开启snapshot isolation,然后再将isolation level设定为snapshot。

 

复制代码
--step1:启用快照隔离
ALTER DATABASE AdventureWorks
    SET ALLOW_SNAPSHOT_ISOLATION ON;
GO

--step2:设置使用快照隔离级别,前面没有设定是因为数据库默认的隔离界别就是Read Committed
SET TRANSACTION ISOLATION LEVEL SNAPSHOT;
GO
复制代码

 

在执行完step1以前,我们可以在sys.databases中查看AdvantureWorks的snapshot_isolation_state和 is_read_committed_snapshot_on这两个属性:

 

SELECT name,snapshot_isolation_state, snapshot_isolation_state_desc, is_read_committed_snapshot_on 
FROM sys.databases where name='AdventureWorks';

 

查询结果如下图所示:

 

 

假如我们不执行step1,只执行step2,然后开启事务进行查询,会报如下错误:

 

Msg 3952, Level 16, State 1, Line 3
Snapshot isolation transaction failed accessing database 'AdventureWorks' because snapshot isolation is not allowed in this database. Use ALTER DATABASE to allow snapshot isolation.

 

执行完step1以后,我们再次查看sys.databases中的内容,发现snapshot_isolation_state由0变为1,如下图所示:

 

 

实验3:使用行版本控制的已提交读隔离(READ_COMMITTED_SNAPSHOT)

 

在此示例中,使用行版本控制的已提交读事务与其他事务并发运行。已提交读事务的行为与快照事务的行为有所不同。与快照事务相同的是,即使其他事务修改了数据,已提交读事务也将读取版本化的行。与快照事务不同的是,已提交读将执行下列操作:

 

  • 在其他事务提交数据更改后,读取修改的数据。
  • 能够更新由其他事务修改的数据,而快照事务不能。

 

query1:事务1

 

复制代码
--实验3:READ_COMMITTED_SNAPSHOT -------------------

--stpe1:启用行版本控制的已提交读
-- 注意运行这句话的时候,不可以有其他连接同时使用AdventureWorks
use master
ALTER DATABASE AdventureWorks
    SET READ_COMMITTED_SNAPSHOT ON;
GO

--step2:设置使用已提交读隔离级别
USE AdventureWorks;
GO
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO

--step3:开启第一个事务
BEGIN TRAN tran1
    --step4:执行select操作,查看VacationHours,对查找的记录加S锁
    SELECT EmployeeID, VacationHours
        FROM HumanResources.Employee 
        WHERE EmployeeID = 4;

    --step8:在事务2中修改了数据以后,在事务1中再次运行查询语句
    --此时查询语句没有被阻塞,返回的值是48,也就是事务2修改之前的数据
    --这是因为事务1是从版本化的行读取数据 
    SELECT EmployeeID, VacationHours
        FROM HumanResources.Employee
        WHERE EmployeeID = 4;

    --step10:在事务2提交以后,事务1再次执行查询操作
    -- 这里和实验2不同,事务1始终返回已提交的值,所以这里返回40,因为会话2已经提交了事务
    SELECT EmployeeID, VacationHours
        FROM HumanResources.Employee
        WHERE EmployeeID = 4;

    --step11:这里修改会成功,不会报错.
    UPDATE HumanResources.Employee
        SET SickLeaveHours = SickLeaveHours - 8
        WHERE EmployeeID = 4;

rollback tran tran1
--实验3结束------------------------------------
复制代码

 

query2:事务2 

 

复制代码
--实验3:READ_COMMITTED_SNAPSHOT  -------------------
USE AdventureWorks;
GO

--step5:开启第二个事务
BEGIN TRAN tran2;
    --step6:修改VacationHours,需要获得更新锁U,在VacationHours上有S锁,US不冲突,因此可以进行修改.
    --在修改VacationHours以后,更新锁U变成了排他锁X
    UPDATE HumanResources.Employee 
        SET VacationHours = VacationHours - 8  
        WHERE EmployeeID = 4;

    -- step7:在当前事务中查询VacationHours,发现只有40小时
    SELECT VacationHours
        FROM HumanResources.Employee
        WHERE EmployeeID = 4;

--step9:提交事务2
commit tran tran2
--实验3结束------------------------------------
复制代码

 

总结:

 

  1. 在事务2修改数据之后,提交之前,事务1中读到的是快照数据,也就是事务2没有修改之前的值。
  2. 在事务2提交修改之后,事务1读到了修改之后的数据。并且事务1可以修改由其他数据修改了的数据。

 

结论

 

  1. 使用 sys.databases 目录视图可以确定两个行版本控制数据库选项的状态。
  2. 对用户表和存储在 master 和 msdb 中的某些系统表的任何更新都会生成行版本。
  3. 在 master 和 msdb 数据库中,ALLOW_SNAPSHOT_ISOLATION 选项自动设置为 ON,并且不能禁用。
  4. 在 master 数据库、tempdb 数据库或 msdb 数据库中,用户不能将 READ_COMMITTED_SNAPSHOT 选项设置为 ON。
  5. 从上面的测试可以看到,原先会发生阻塞的两个会话在使用行版本控制的隔离级别后,都不会遇到阻塞了。但是两种行版本控制的结果又有不同。可以用表1来总结。

 

表1  使用行版本控制隔离级别后的不同

 

会话1

会话2

结果

A. 普通已提交事务

B. 使用快照隔离

C. 使用行版本控制的已提交读

BEGIN TRAN

查询1

 

48

 

BEGIN TRAN

修改1

成功

 

查询1

40

查询2

 

被阻塞

48

 

COMMIT TRAN

查询2返回40

 

查询3

 

40

48

40

修改2

ROLLBACK TRAN

 

成功

失败

成功

 

 补充:如果要查看锁状态,可以使用如下两种方法:

 

复制代码
--常看锁状态有一下两种方式
--1.查看视图sys.dm_tran_locks
SELECT request_session_id, resource_type, resource_associated_entity_id,
        request_status, request_mode, resource_description
    FROM sys.dm_tran_locks

--2.使用存储过程
sp_lock
复制代码

 

 

 

 

 

 

分享到:
评论

相关推荐

    SQLSERVER快照隔离

    这样,在读取数据时,数据库可以根据事务的隔离级别和读取时机,选择合适的行版本,保障读取的一致性。 快照隔离级别提供了两种模式:语句级快照隔离(Statement-Level Snapshot Isolation)和事务级快照隔离...

    SQLserver锁和事务隔离级别的比较与使用[定义].pdf

    在 SQL Server 中,事务隔离级别是一种机制,它可以控制事务之间的相互干扰。SQL Server 中有四种事务隔离级别:未提交读、提交读、可重复读和串行化。 未提交读是最低的隔离级别,在这个级别下,事务可以读取其他...

    SQLserver锁和事务隔离级别的比较与使用

    在SQL Server中,锁主要用于控制多个用户同时访问同一数据时的并发操作,而事务隔离级别则是确定在事务中如何处理这些并发操作的标准。 首先,我们要理解共享锁和排他锁的概念。共享锁(Shared Locks)也称为读锁,...

    SQLSERVER事务隔离级别的实验研究.pdf

    但在SQL Server中,可重复读隔离级别仍允许幻影行的存在,即在读取时满足条件的新数据行可能被创建,这可能导致事务读取到新的数据行。 4. 可序列化(SERIALIZABLE):此隔离级别提供了最高等级的隔离,保证了事务在...

    SQL Server 2002中的事务隔离级别.pdf

    SQL Server 2002中的事务隔离级别是数据库管理系统中非常关键的概念,它涉及到多用户同时访问数据库时的数据一致性与并发性控制。事务是数据库操作的基本单元,它保证了一组数据库操作要么全部成功,要么全部回滚,...

    sqlserver数据库精简版

    3. **安全性**:SQL Server提供了严格的用户权限管理和角色分配,可以设置对象级别的权限,确保数据的安全性。 4. **备份与恢复**:SQL Server支持多种备份类型,如完整备份、差异备份和日志备份,以及多种恢复模式...

    关于SQL Server事务隔离级别的探讨.pdf

    SQL Server事务隔离级别是数据库管理系统中非常重要的一个概念,它主要用于控制并发事务的执行,确保数据的一致性和完整性。事务隔离级别可以避免并发事务中的各种问题,如脏读、不可重复读和幻读。了解和应用不同的...

    怎么理解SQL SERVER中事务隔离级别及相应封锁机制.pdf

    理解SQL Server中事务隔离级别及相应封锁机制 SQL Server中事务隔离级别是数据库管理系统中一个重要的概念,它决定了事务之间的并发执行和数据的一致性。本文将详细介绍SQL Server中事务隔离级别的概念、分类、特点...

    SQL Server 2000 并发控制

    ### SQL Server 2000 并发控制 #### 实验目标 ...通过上述实验操作,学生将能够深入了解SQL Server 2000中的并发控制机制,包括不同类型的锁、事务隔离级别以及如何有效地避免和解决并发操作中可能出现的问题。

    SQL Server Native Client 10.0.zip

    它支持多种高级特性,包括SQL Server特有的事物隔离级别、游标、预编译的SQL语句(存储过程)以及批量数据传输等。 在Django这样的Python Web框架中,如果需要连接到SQL Server 10.0,SQL Server Native Client可以...

    SQL Server事务的控制与并发处理视频

    通过学习这个视频教程,你可以掌握如何在SQL Server中有效地控制事务,理解不同隔离级别的优缺点,并学会在并发环境中解决可能出现的问题。通过“事务控制与并发处理一.exe”和“事务控制与并发处理二.exe”的实践...

    深入解析sqlserver 2008 Microsoft SQL Server 2008 Internals

    3. **事务与并发控制**:书中深入剖析了SQL Server 2008的事务管理机制,包括ACID属性、快照隔离级别和行版本控制,以及并发问题如死锁检测和避免。 4. **备份与恢复**:备份和恢复是数据库管理的重要环节,书中...

    SQL Server JDBC Jar包

    4. **事务支持**:JDBC驱动支持多种事务隔离级别,允许开发者根据应用需求控制事务的并发性和一致性。 5. **Unicode支持**:全面支持Unicode字符集,使得跨语言和地区的数据处理成为可能。 6. **高级查询功能**:...

    sqlserver 2008 jdbc 驱动包

    同时,驱动还支持一些高级特性,如批处理、游标、存储过程调用、事务隔离级别设置等,以满足复杂的应用场景需求。因此,对于那些基于Java平台并需要与SQL Server 2008交互的项目,这个驱动包是不可或缺的工具。

    SQL Server 2000完结篇系列之三:数据并发-彻底掌握SQL Server 2000事务机制

    在SQL Server 2000中,事务管理是数据库系统中至关重要的部分,因为它涉及到数据的一致性、完整性和并发控制。本篇将深入探讨SQL Server 2000的事务机制,帮助你全面理解并掌握如何在多用户环境下有效地处理数据并发...

    SQL SERVER中事务隔离级别的实例分析.pdf

    在SQL Server数据库管理系统中,事务隔离级别是确保数据一致性、完整性和并发控制的重要机制。事务在执行时可能会遇到多种并发问题,如脏读、不可重复读和幻读,这些问题都会影响数据的正确性和系统的稳定性。SQL ...

    Microsoft SQL Server 2008 Internals (PDF 高清版)

    2. **锁和并发控制**:深入分析SQL Server的锁定机制,包括不同的锁定类型、死锁处理、事务隔离级别等内容。 3. **存储引擎**:解析SQL Server如何存储数据,涉及页结构、索引类型及其维护机制。 4. **查询优化器**...

    SQL Server 2005升级时的兼容性级别检查.pdf

    从SQL Server 2000升级到SQL Server 2005时,如果不改变兼容性级别,那么新版本的T-SQL特性及查询优化器的性能提升将不会得到应用。 首先需要了解的是,SQL Server 2000的默认兼容性级别是80,而SQL Server 2005的...

    SqlServer并发调优

    综上所述,SQL Server并发调优涉及到多个层面的技术细节,包括但不限于锁的合理使用、事务隔离级别的正确选择、数据版本控制的应用以及锁策略的动态调整。理解和掌握这些核心概念和技术,对于优化数据库性能、提升...

    Win10可用的Microsoft SQL Server 2008 Native Client

    - 更新驱动程序:考虑到SQL Server有新的版本发布,定期检查并更新到最新版本的Native Client以保持最佳兼容性和安全性。 总之,Microsoft SQL Server 2008 Native Client是连接和管理SQL Server数据库的关键组件,...

Global site tag (gtag.js) - Google Analytics