SQL Server NOLOCK 提示会返回部分写入的行吗?

发布于 2024-08-06 17:15:57 字数 148 浏览 13 评论 0原文

首先,我不是问是否应该使用 NOLOCK。让我们过去吧。

我想问题归结为sql server如何写入数据?是一次写入一整行还是一次写入一列?

我这样问是因为正在考虑 NOLOCK 提示。只要返回(或不返回)整行,脏读就可以。部分写入的行是不可接受的。

First of all I'm not asking if NOLOCK should or should not be used. Let's get past that.

I guess the question comes down to how sql server writes data? Is an entire row written at once or does it write it a column at a time?

I'm asking because the NOLOCK hint is being considered. A dirty read is fine as long as the entire row is returned (or not returned). Partially written rows are not acceptable.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

农村范ル 2024-08-13 17:15:57

不会。插入、更新和删除等数据修改操作受低级物理保护 闩锁。所有数据访问操作,包括无锁 SELECT,都必须遵守锁存协议。结果是任何读者都看不到部分写入。

No. Data modification operations like inserts, updates and deletes are protected by low level physical Latches. All data access operations, including lock-free SELECT, are obliged to conform to the latching protocol. The result is that partial writes are never seen by any reader.

蹲墙角沉默 2024-08-13 17:15:57

与其他两个答案中所说的相反:您可能会被撕裂。

一种情况是当您有键查找时,因此非聚集索引具有旧的数据的版本和聚集索引具有新版本。

第二种情况是 LOB 列,因此任何 varchar(max) nvarchar(max) varbinary(max) xml text ntext image 列。

由于锁存(这与锁定分开并且无法禁用),单个页面无法同时更新,但可以同时写入多个页面。这对于行内数据来说不是问题,因为一行不能跨越多个页面,它仅在 LOB 数据上体现,即使它正好适合一页,因为它位于行外。

此链接:

创建脚本

CREATE TABLE dbo.SingleRow
(
    RowID int NOT NULL,
    LOB1 varchar(max) NOT NULL,
    LOB2 varchar(max) NOT NULL,
    CONSTRAINT pk_SingleRow PRIMARY KEY CLUSTERED(RowID)
);

INSERT dbo.SingleRow(RowID,LOB1,LOB2) VALUES(1,
  REPLICATE(CONVERT(varchar(max),'X'),16100),
  REPLICATE(CONVERT(varchar(max),'X'),16100));

会话 1

SET NOCOUNT ON;

DECLARE @AllXs varchar(max) = REPLICATE(CONVERT(varchar(max), 'X'), 16100),
        @AllYs varchar(max) = REPLICATE(CONVERT(varchar(max), 'Y'), 16100); 

WHILE 1 = 1
BEGIN
    UPDATE dbo.SingleRow
    SET LOB1 = @AllYs, LOB2 = @AllYs
    WHERE RowID = 1;

    UPDATE dbo.SingleRow
    SET LOB1 = @AllXs, @LOB2 = @AllXs
    WHERE RowID = 1;
END;

会话 2

DECLARE @bad_row int = 0, 
        @bad_tuple int = 0,
        @LOB1 varchar(max) = '',
        @LOB2 varchar(max) = '',
        @AllXs varchar(max) = REPLICATE(CONVERT(varchar(max), 'X'), 16100),
        @AllYs varchar(max) = REPLICATE( CONVERT(varchar(max), 'Y'), 16100);

WHILE @bad_row + @bad_tuple < 2
BEGIN
    SELECT @LOB1 = LOB1, @LOB2 = LOB2
      FROM dbo.SingleRow WITH(NOLOCK)
      WHERE RowID = 1;

    IF @LOB1 <> @LOB2 AND (@LOB1 IN (@AllXs,@AllYs)) AND (@LOB2 IN (@AllXs,@AllYs))
    BEGIN
      -- corrupt row (one column before an update, one column after update)
      SET @bad_row = 1;
      PRINT 'Corrupt row. LOB1 = ' + LEFT(@LOB1, 10) + '...' + RIGHT(@LOB1, 10)
          + '..., LOB2 = ' + LEFT(@LOB2, 10) + '...' + RIGHT(@LOB2, 10);

      BREAK;
    END

    IF @LOB1 NOT IN (@AllXs, @AllYs) 
    BEGIN
      -- corrupt tuple (one LOB page from before an update, one LOB page from after)
      SET @bad_tuple = 1;
      PRINT 'Corrupt value. LOB1 = ' + LEFT(@LOB1, 10) + '...' + RIGHT(@LOB1, 10);

      BREAK;
    END
END;

另请注意,NOLOCK 还有许多其他效果,尤其是您可以读取同一行两次,或者根本不读取,并且在不同的引用中会有不同的情况同一张表的。这可能会影响需要唯一连接的查询,这可能会使连接增加一倍。

Contrary to what is said in the other two answers: You can get torn reads.

One case is when you have a Key Lookup, so the non-clustered index has the old version of the data and the clustered index has the new version.

A second case is on LOB columns, so any varchar(max) nvarchar(max) varbinary(max) xml text ntext image columns.

A single page cannot be updated simultaneously, due to latching (this is separate from locking and cannot be disabled), but multiple pages can be written simultaneously. This is not an issue with in-row data, because a row cannot span multiple pages, it only manifests on LOB data, even if it fits on exactly one page, because it lives off-row.

This is demonstrated in this link:

Creation script

CREATE TABLE dbo.SingleRow
(
    RowID int NOT NULL,
    LOB1 varchar(max) NOT NULL,
    LOB2 varchar(max) NOT NULL,
    CONSTRAINT pk_SingleRow PRIMARY KEY CLUSTERED(RowID)
);

INSERT dbo.SingleRow(RowID,LOB1,LOB2) VALUES(1,
  REPLICATE(CONVERT(varchar(max),'X'),16100),
  REPLICATE(CONVERT(varchar(max),'X'),16100));

Session 1

SET NOCOUNT ON;

DECLARE @AllXs varchar(max) = REPLICATE(CONVERT(varchar(max), 'X'), 16100),
        @AllYs varchar(max) = REPLICATE(CONVERT(varchar(max), 'Y'), 16100); 

WHILE 1 = 1
BEGIN
    UPDATE dbo.SingleRow
    SET LOB1 = @AllYs, LOB2 = @AllYs
    WHERE RowID = 1;

    UPDATE dbo.SingleRow
    SET LOB1 = @AllXs, @LOB2 = @AllXs
    WHERE RowID = 1;
END;

Session 2

DECLARE @bad_row int = 0, 
        @bad_tuple int = 0,
        @LOB1 varchar(max) = '',
        @LOB2 varchar(max) = '',
        @AllXs varchar(max) = REPLICATE(CONVERT(varchar(max), 'X'), 16100),
        @AllYs varchar(max) = REPLICATE( CONVERT(varchar(max), 'Y'), 16100);

WHILE @bad_row + @bad_tuple < 2
BEGIN
    SELECT @LOB1 = LOB1, @LOB2 = LOB2
      FROM dbo.SingleRow WITH(NOLOCK)
      WHERE RowID = 1;

    IF @LOB1 <> @LOB2 AND (@LOB1 IN (@AllXs,@AllYs)) AND (@LOB2 IN (@AllXs,@AllYs))
    BEGIN
      -- corrupt row (one column before an update, one column after update)
      SET @bad_row = 1;
      PRINT 'Corrupt row. LOB1 = ' + LEFT(@LOB1, 10) + '...' + RIGHT(@LOB1, 10)
          + '..., LOB2 = ' + LEFT(@LOB2, 10) + '...' + RIGHT(@LOB2, 10);

      BREAK;
    END

    IF @LOB1 NOT IN (@AllXs, @AllYs) 
    BEGIN
      -- corrupt tuple (one LOB page from before an update, one LOB page from after)
      SET @bad_tuple = 1;
      PRINT 'Corrupt value. LOB1 = ' + LEFT(@LOB1, 10) + '...' + RIGHT(@LOB1, 10);

      BREAK;
    END
END;

Note also that there are many other effects of NOLOCK, not least that you can read the same row twice, or not at all, and differently in different references of the same table. This might affect queries which expect a unique join, which could double up your joins.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文