在冗长的UPDATE期间进行SELECT - 对于不同的事务隔离级别和SELECT WITH(NOLOCK),SELECT会发生什么?

时间:2014-07-24 19:32:05

标签: sql sql-server sql-server-2012

鉴于UPDATE执行需要5分钟左右,当SELECT尝试从同一个表中检索数据时会发生什么?对于不同的事务隔离级别和SELECT WITH (NOLOCK),SELECT是否等待更新?如果没有,SELECT是返回旧数据(UPDATE之前的数据)还是部分当前插入的记录(例如当前插入的50%的记录)?

如果发现以下问题,但它只描述了在长SELECT期间执行和UPDATE时会发生什么。

  

SQL Server - does [SELECT] lock [UPDATE]?

我正在使用MS SQL Server 2012.希望这种行为对于不同的实现是一致的。

2 个答案:

答案 0 :(得分:3)

  • READ UNCOMMITTEDSELECT可以阅读各种令人讨厌的不一致之处。旧行,新行,重复行,缺少行。它也可以完全错误地用着名的数据运动"错误。
  • READ COMMITTED:将阻止没有快照隔离。将以完美的一致性返回具有快照隔离的旧状态。
  • REPEATABLE READ/SERIALIZABLE:会阻止。
  • SNAPSHOT:将以完全一致的方式返回具有快照隔离的旧状态。

听起来你应该阅读一些并发教程。我已经写了这些简短的事实来帮助你入门。为了真正理解你能做出预测的事情(实现了),你需要深入了解Stack Overflow可以提供的答案。

大多数情况下,您希望将SNAPSHOT用于只读事务。它消除了所有并发问题。请注意它有一些缺点。

答案 1 :(得分:2)

This post by Gavin Draper explains it quite well and contains some example query's.

  

按示例的SQL Server隔离级别

     

SQL Server中的隔离级别控制锁定之间的工作方式   交易。

     

SQL Server 2008支持以下隔离级别

     
      
  • 未提交阅读
  •   
  • 读取已提交(默认)
  •   
  • 可重复阅读
  •   
  • Serializable
  •   
  • 快照
  •   
     

在详细介绍其中每一项之前,您可能需要创建一个   新数据库运行示例,在新的上运行以下脚本   数据库创建示例数据。注意:你也想放弃   在每个示例之前,将IsolationTests表重新运行此脚本   重置数据。

CREATE TABLE IsolationTests  
(
    Id INT IDENTITY,
    Col1 INT,
    Col2 INT,
    Col3 INTupdate te
)

INSERT INTO IsolationTests(Col1,Col2,Col3)  
SELECT 1,2,3  
UNION ALL SELECT 1,2,3  
UNION ALL SELECT 1,2,3  
UNION ALL SELECT 1,2,3  
UNION ALL SELECT 1,2,3  
UNION ALL SELECT 1,2,3  
UNION ALL SELECT 1,2,3
     

在我们进一步研究之前,了解这两者是很重要的   术语...

     
      
  1. 脏读 - 这是当您读取未提交的数据时,执行此操作时无法保证将提交数据读取   这意味着数据可能很糟糕。
  2.   
  3. 幻像读取 - 这是您使用的数据自您第一次读取以来已被另一个事务更改的时间。   这个   意味着在同一事务中随后读取此数据即可   好吧不一样。
  4.         

    Read Uncommitted

         

    这是最低的隔离级别。阅读未提交的原因   没有要求的共享锁,允许您读取数据   目前正在其他交易中进行修改。它还允许其他   用于修改您正在阅读的数据的事务。

         

    你可以想象这会导致一些意想不到的结果   各种不同的方式。例如,select返回的数据   如果更新在另一个中运行,则可能处于中途状态   导致某些行返回更新的事务   价值观,有些则没有。

         

    要查看未提交的读操作,请在一个选项卡中运行Query1   Management Studio然后在另一个选项卡中快速运行Query2   Query1完成。

         

    查询1

    BEGIN TRAN  
    UPDATE IsolationTests SET Col1 = 2  
    --Simulate having some intensive processing here with a wait
    WAITFOR DELAY '00:00:10'  
    ROLLBACK
    
         

    QUERY2

    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED  
    SELECT * FROM IsolationTests
    
         

    请注意,Query2不会等待Query1完成,也会更多   重要的是Query2返回脏数据。记住Query1回滚所有   但是它的更改无论如何Query2都返回了数据,这是   因为它没有等待所有其他独家交易   锁定这些数据,它只返回当时的数据。

         

    使用read查询数据有一个语法快捷方式   使用NOLOCK表提示的未提交隔离级别。您   可以将上面的Query2更改为这样,它会执行   完全相同的事情。

    SELECT * FROM IsolationTests WITH(NOLOCK)
    
         

    Read Committed

         

    这是默认的隔离级别,意味着选择只会返回   提交的数据。 Select语句将发出共享锁请求   对于您正在查询的数据,这会导致您等待另一个   事务已经对该数据进行了独占锁定。一旦你有了   您的共享锁定任何其他尝试修改该数据的事务   将要求独家锁定并等待您的阅读   承诺的交易完成。

         

    您可以看到等待修改的读取事务的示例   在通过运行返回数据之前完成事务   跟随“读取未提交”一样,在单独的选项卡中进行查询。

         

    查询1

    BEGIN TRAN  
    UPDATE Tests SET Col1 = 2  
    --Simulate having some intensive processing here with a wait
    WAITFOR DELAY '00:00:10'  
    ROLLBACK
    
         

    QUERY2

    SELECT * FROM IsolationTests
    
         

    注意Query2如何等待第一个事务完成之前   返回以及返回的数据如何是我们开始的数据   与Query1进行回滚。没有隔离级别的原因   指定是因为Read Committed是默认的隔离级别   SQL Server。如果要检查正在运行的隔离级别   你可以运行DBCC useroptions。记住隔离级别   连接/事务特定,因此不同的查询相同   数据库通常在不同的隔离级别下运行。

         

    可重复读取

         

    这与Read Committed类似,但附加保证   如果您在交易中发出两次相同的选择,您将获得   两次都是一样的结果。它通过坚持共享来做到这一点   锁定它在读取的记录上获取,直到结束   transaction,这意味着任何尝试修改这些的事务   记录被迫等待读取事务完成。

         

    之前运行Query1然后运行它运行Query2

         

    查询1

    SET TRANSACTION ISOLATION LEVEL REPEATABLE READ  
    BEGIN TRAN  
    SELECT * FROM IsolationTests  
    WAITFOR DELAY '00:00:10'  
    SELECT * FROM IsolationTests  
    ROLLBACK
    
         

    QUERY2

    UPDATE IsolationTests SET Col1 = -1
    
         

    请注意,即使查询,Query1也会为两个选择返回相同的数据   您运行查询以在第二次选择运行之前修改数据。这个   是因为更新查询被迫等待Query1完成   由于您指定打开的独占锁定   可重复阅读。

         

    如果您重新运行上述查询但将Query1更改为Read Committed you   会注意到两个选择返回不同的数据,而Query2也是如此   不要等待Query1完成。

         

    关于可重复读取的最后一件事是数据可以   如果添加更多记录,则在2个查询之间切换。可重复阅读   保证先前选择查询的记录不会被更改或   删除,它不会停止插入新记录,因此它仍然是   很有可能在此隔离级别获得幻影读取。

         

    序列化

         

    此隔离级别采用可重复读取并添加保证   不会添加任何新数据,从而消除了获取Phantom的可能性   读取。它通过在查询数据上放置范围锁来实现此目的。这个   导致尝试修改或插入数据的任何其他事务   在此交易中等待它完成。

         

    你现在知道这个问题并排运行......

         

    查询1

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE  
    BEGIN TRAN  
    SELECT * FROM IsolationTests  
    WAITFOR DELAY '00:00:10'  
    SELECT * FROM IsolationTests  
    ROLLBACK
    
         

    QUERY2

    INSERT INTO IsolationTests(Col1,Col2,Col3)  
    VALUES (100,100,100)
    
         

    您将看到Query2中的插入等待Query1完成   在它运行之前消除了幻读的可能性。如果你改变了   在Query1中的隔离级别可重复读取,你会看到   插入不再被阻塞和Query1中的两个select语句   返回不同的行数。

         

    快照

         

    这提供了与可序列化相同的保证。那是什么呢?   区别?嗯它的工作方式更多,使用快照不是   阻止其他查询插入或更新由其触及的数据   快照事务。而是使用行版本控制,所以当数据是   改变了旧版本保存在tempdb中的现有事务   将看到没有更改的版本。当所有交易即   在更改完成之前启动上一行版本   从tempdb中删除。这意味着即使另一个交易也有   进行更改后,您将始终获得与第一次相同的结果   该交易的时间。

         

    所以从正面来看,你没有阻止任何其他人修改   您运行交易时的数据,但....你正在使用额外的   SQL Server上的资源,用于保存更改的多个版本。

         

    要使用快照隔离级别,您需要在其上启用它   数据库通过运行以下命令

    ALTER DATABASE IsolationTests  
    SET ALLOW_SNAPSHOT_ISOLATION ON
    
         

    如果从可序列化中重新运行示例但更改隔离   级别到快照您将注意到您仍然获得相同的数据   返回但Query2不再等待Query1完成。

         

    摘要

         

    你现在应该知道每个不同的隔离方式   水平工作。你可以看到你使用的等级越高越好   你提供的并发性以及你带来的阻塞越多   表。您应该始终尝试使用最低的隔离级别   这通常是承诺的。