带有 nolocks 和 TRANSACTION ISOLATION LEVEL READ COMMITTED 的 SQL 事务

Posted

技术标签:

【中文标题】带有 nolocks 和 TRANSACTION ISOLATION LEVEL READ COMMITTED 的 SQL 事务【英文标题】:SQL Transactions with nolocks and TRANSACTION ISOLATION LEVEL READ COMMITTED 【发布时间】:2014-06-17 20:19:36 【问题描述】:

我有一个这样的存储过程。

CREATE PROCEDURE [dbo].[mysp] 
AS
SET NOCOUNT    ON
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN
BEGIN TRAN

DECLARE @TrackingCode INT   

SELECT @TrackingCode = DefaultsData 
FROM dbo.Defaults  
WHERE DefaultsID=77

UPDATE dbo.Defaults
SET DefaultsData = @PassedTrackingCode+1
WHERE DefaultsID=77

SELECT @TrackingCode

COMMIT TRAN
END

假设我们同时(同时)执行这个存储过程两次,那么在这两个时间返回的@TrackingCode 值将是多少。如果我在存储过程中的SELECT 语句上使用NOLOCK 会怎样。

【问题讨论】:

如果您希望它以可预测的方式运行,您需要使用 HOLDLOCK 进行选择 您正在使用READ COMMITTED 隔离级别。应该没问题,我认为您不会得到不一致的结果。运行它,看看你自己。 【参考方案1】:

1) 如果您的目的是生成唯一的跟踪代码,那么这是一个坏主意。你可以做这个简单的测试:

CREATE TABLE dbo.Defaults (
    DefaultsData INT,
    DefaultsID INT PRIMARY KEY
);
GO
INSERT INTO dbo.Defaults VALUES (21, 77);
GO

CREATE PROCEDURE [dbo].[mysp] 
AS
SET NOCOUNT    ON
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
BEGIN
BEGIN TRAN

DECLARE @TrackingCode INT   

SELECT @TrackingCode = DefaultsData 
FROM dbo.Defaults  
WHERE DefaultsID=77

WAITFOR  DELAY '00:00:05' -- 5 seconds delay
UPDATE dbo.Defaults
SET DefaultsData = @TrackingCode+1 
WHERE DefaultsID=77

SELECT @TrackingCode -- Maybe it should return the new code

COMMIT TRAN
END;
GO

然后在 SQL Server Management Studio 中打开一个新窗口 (Ctrl + N) 并执行 (F5)

EXEC [dbo].[mysp] 

并打开第二个新窗口并执行(不到 5 秒)

EXEC [dbo].[mysp] 

在这种情况下,您将获得相同的值NOLOCK 是个坏主意(一般来说),在这种情况下对您没有帮助。

2) 如果(我再说一遍,对不起)您的意图是生成唯一的跟踪代码,那么您可以使用

ALTER PROCEDURE [dbo].[mysp] 
    @TrackingCode INT OUTPUT 
AS
BEGIN
    SET NOCOUNT ON;

    UPDATE  dbo.Defaults
    SET @TrackingCode = DefaultsData = DefaultsData + 1 -- It generate the new code
    WHERE   DefaultsID=77;
END;
GO

或者您可以使用sequences (SQL Server 2012+)。

【讨论】:

我不想使用唯一的跟踪代码。我希望每个尝试执行 sp 的用户都需要生成一个新的跟踪号,而不是相同的跟踪号。 但是当用户同时执行这个sp时,他们都得到了相同的tracking number。 我认为您建议的第二个选项会起作用。会试一试。谢谢博格丹! @user1176058:是的。您的版本可以轻松生成重复的代码(参见 #1)。试试我的解决方案(见 #2)。

以上是关于带有 nolocks 和 TRANSACTION ISOLATION LEVEL READ COMMITTED 的 SQL 事务的主要内容,如果未能解决你的问题,请参考以下文章

使用 nolock 的 Java Hibernate HQL 查询

带有 (NOLOCK) 提示的 SQL 服务器

Could not continue scan with NOLOCK due to data movement

使用 nolock 和独占锁

使用 WITH (NOLOCK) 和事务

在Linq中使用ExecuteCommand到带有Transaction的Sql