代码之家  ›  专栏  ›  技术社区  ›  Gennady Vanin Геннадий Ванин Mikael Svenson

当SELECT WITH(NOLOCK)提示时,是否防止半写值读取?

  •  0
  • Gennady Vanin Геннадий Ванин Mikael Svenson  · 技术社区  · 14 年前

    我认为这是DBMS之间的问题,尽管我在SQL Server术语中指定了这个问题。

    在阅读了msdn文档之后,例如,[1],我无法理解:

    是否可以用(NOLOCK)值选择半写的、部分覆盖的、-updated、-deleted、-inserted)值,如果不能,如何防止(半写的、读取的)值(如果不考虑锁)?

    读取半写值违反了哪一个DBMS原则?
    我在确定它的术语时遇到困难(是否一致、完整性中断)?
    对应术语的名称是什么?

    更新:
    我用NOLOCK删除了更新(删除)的问题。

    例如,msdn docs,[1]和多篇文章告诉SELECT WITH(NOLOCK)与READUNCOMMITTED相同,并且“不会发出共享锁来阻止其他事务修改当前事务读取的数据,并且由其他事务设置的独占锁不会阻止当前事务读取锁定的数据”。

    我是否正确理解DBMS确保只能读取完全写入(提交或不提交)的值?
    如何确保没有使用或遵守锁?
    这个问题不在于哪个事务可以读取什么以及何时读取,而在于如何防止读取未完全写入的值。

    更新2:
    由于这个问题开始被否决并关闭,我将有关更新(删除)和(NOLOCK)的问题移到msdn论坛:

    我还在msdn论坛上重复了同样的问题:

    造成了完全的混乱。
    但是,为什么它(在这里被关闭不是一个有答案的问题)?
    它是一个非常基本的基本概念,答案简单,对于数据库开发人员和dba来说是必须的。

    [一] 表提示(Transact-SQL)
    SQL Server 2008 R2
    http://msdn.microsoft.com/en-us/library/ms187373.aspx

    2 回复  |  直到 14 年前
        1
  •  4
  •   Gary Myers    14 年前

    Oracle在任何情况下都不允许脏读(即读取另一个会话的未提交值)。它违反了“孤立”(I in ACID )对于数据库,并且可能会给读取操作带来明显的不一致性[例如看到没有父记录的子记录]。

    有两种机制在起作用。 首先,每条记录都有一个锁定字节,指示它当前是否被锁定。字节的值指向块头中的事务,因此会话可以确定锁是自己的还是属于另一个会话。 如果读取看到字节已设置,则它使用块头中的指针查找块的旧版本。如果仍处于锁定状态,则它将继续跟踪指针,直到到达记录显示为未锁定的块的版本。然后返回值。

    同样的机制也用于基于时间的一致性。如果select在下午3点开始,并在下午3点02分发现一个被修改的块,那么它将跟踪历史返回以查找3点时当前的块版本。然后,它可能会发现它想要的记录在下午3:00被锁定[可能在下午3:01提交],并且必须进一步返回以查看下午3:00提交的值是多少。

    另一种保护机制是闩锁。当它读取一个块时,它会在读取期间使用一个锁存器。这将防止另一个进程(可能在另一个CPU上运行)在读取期间访问块(即进程A不能在线程B读取块的同时设置锁字节-它必须等待读取完成)。这些锁存是非常低级别的CPU操作,仅保持很短的持续时间。在一个单核/cpu盒上,不需要锁存,因为只有一个核,所以只有线程上的核可以同时执行。

        2
  •  0
  •   Community Patrick Maupin    7 年前

    经过一些思考和实验,我相信DBMS并没有提供这样的价值完整性:

    • 如果我们假设了这种可能性,那么我们立即得出结论:在同一事务范围内的同一事务可以使用未完全定义(写入、插入、更新、删除)的值,而这些值实际上永远不会发生;
    • 值不仅由DBMS使用,而且由操作系统和其他软件框架使用

    它很可能是在操作系统上实现的事务特性,或者更低级(机器、硬件)的事务特性。

    更新:
    这个 Gary's answer 支持它:

    “这些锁存是非常低级别的CPU操作,只能保持很短的时间。”

    不过,我并不打算将DBMS事务隔离现象的讨论与硬件提供的低级事务支持混为一谈。

    更新2:
    而且,如何更好地命名相应的低级(硬件事务支持)术语,以避免其与DBMS事务术语的混淆和不一致?它是价值一致性还是价值完整性?

    更新3:
    假设我在nvarchar(max)中有2GB字符串,内存为1GB。CPU如何在硬件级别为这个值提供完整性?

    msdn线程中Razvan Socol的答案 Is a half-written values reading prevented WITH (NOLOCK) hint? 提供一个脚本来捕获部分更新值的读取。该站点当前已关闭,我在此处重新生成以下代码:

    1)创建一个测试表并填充其中的10行:

    if object_id('Test') IS not NULL
    drop table Test;
    
    CREATE TABLE Test (
      ID int IDENTITY PRIMARY KEY,
      Txt nvarchar(max) NOT NULL
    )
    GO
    -----------
    INSERT INTO Test
    SELECT REPLICATE(CONVERT(nvarchar(max), 
         CHAR(65+ABS(CHECKSUM(NEWID()))%26)),100000)
    GO 10
    ---
    

    2个)
    在第一个会话(SSMS的选项卡)中启动耗时的更新:

    UPDATE Test 
    SET Txt=REPLICATE(CONVERT(nvarchar(max),
               CHAR(65+ABS(CHECKSUM(NEWID()))%26)),100000)
    
    GO 1000 
    

    三)
    在第二个会话(SSMS的选项卡)中启动捕获半覆盖值:

    WHILE 1=1 BEGIN
      SELECT Txt FROM Test WITH (NOLOCK) 
      WHERE LEN(REPLACE(Txt,LEFT(Txt,1),''))<>0;
      select 'rowcount inside=',@@rowcount;
      IF @@ROWCOUNT<>0 BREAK
    END
    --for wishing to try it in non-SqlServer DBMS
    -- WITH(NOLOCK) hint is another way as setting READ UNCOMMITTED tx iso level
    --SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    

    好吧,这是SQL Server 2008 R2的SSM很快捕获了一堆半覆盖的值。
    我很好奇其他数据库管理系统的结果是什么?