TSQL在存储过程中的互斥访问

vto*_*ola 3 sql-server multithreading synchronization locking sql-server-2005

多个Web服务器访问SQL Server以获取数字代码,当此代码不存在时,它必须由SQL Server自动生成.

我需要确保即使两个并发调用进入并且代码不存在,也只创建一个代码,并且两个调用都返回相同的代码.所以我必须做这样的事情:

begin lock
  if code exists
    return code
  else
    generate code
    return code
end lock
Run Code Online (Sandbox Code Playgroud)

我一直在阅读关于隔离级别和表锁定的一些内容,但我对这一切都很糟糕.首先,我认为SERIALIZABLE隔离级别是我需要的,但显然它不是.

那么,你将如何在TSQL中实现"锁定"?

非常感谢.

更新:

当我尝试使用示例设置可序列化级别时出现此错误:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE get_code 
AS
BEGIN
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
    GO
    BEGIN TRANSACTION

    select code from codes where granted is null;
END
GO
Run Code Online (Sandbox Code Playgroud)

消息1018,级别15,状态1,过程get_code,第4行'SERIALIZABLE'附近的语法不正确.如果这是作为表提示的一部分,则现在需要A WITH关键字和括号.有关正确的语法,请参阅SQL Server联机丛书.消息102,级别15,状态1,行5'END'附近的语法不正确.

这是什么意思?

gbn*_*gbn 9

SERIALIZABLE是锁定的隔离级别,而不是信号量.

在这种情况下它不起作用你所做的只是在TXN的末尾保持读锁定,这不会阻止另一个进程进入代码读取.

您需要在事务模式下使用sp_getapplock.您可以将其配置为等待,立即炸弹等:由您决定

这是基于我的包含TRY CATCH ROLLBACK模式的嵌套存储过程的模板

ALTER PROCEDURE get_code 
AS
SET XACT_ABORT, NOCOUNT ON

DECLARE @starttrancount int, @result int;

BEGIN TRY
    SELECT @starttrancount = @@TRANCOUNT

    IF @starttrancount = 0 BEGIN TRANSACTION

    EXEC @result = sp_getapplock 'get_code', 'Exclusive', 'Transaction', 0 
    IF @result < 0
        RAISERROR('INFO: One at a time please`!', 16, 1);

    [...Perform work...]


    IF @starttrancount = 0 
        COMMIT TRANSACTION
    ELSE
        EXEC sp_releaseapplock 'get_code';
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 AND @starttrancount = 0 
        ROLLBACK TRANSACTION
    RAISERROR [rethrow caught error using @ErrorNumber, @ErrorMessage, etc]
END CATCH
GO
Run Code Online (Sandbox Code Playgroud)