多个Web服务器访问SQL Server以获取数字代码,当此代码不存在时,它必须由SQL Server自动生成。
我需要确保即使两个并发调用进入且代码不存在,也只创建一个代码,并且两个调用都返回相同的代码。所以我必须做这样的事情:
begin lock
if code exists
return code
else
generate code
return code
end lock
我一直在阅读一些关于隔离级别和表锁定的内容,但是我对这一切都很糟糕。首先,我认为SERIALIZABLE隔离级别是我需要的,但显然它不是。
那么,你会怎样做才能在TSQL中实现“锁定”?
非常感谢。
更新:
当我尝试使用this设置可序列化级别时出现此错误:
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
Msg 1018,Level 15,State 1,Procedure get_code,第4行附近的语法不正确 'SERIALIZABLE'。如果是这样的话 表格提示的一部分,一个WITH关键字 现在需要括号和括号。看到 SQL Server联机丛书正确 句法。 Msg 102,Level 15,State 1, 第5行'END'附近的语法不正确。
这意味着什么?
答案 0 :(得分:9)
SERIALIZABLE是锁定的隔离级别,而不是semaphore。
在这种情况下它不会起作用你所要做的就是在TXN的末尾保持读锁定,这不会阻止另一个进程进入代码读取。
您需要在事务模式下使用sp_getapplock。您可以将其配置为等待,立即炸弹等:由您决定
这是基于我Nested stored procedures containing TRY CATCH ROLLBACK pattern?
的模板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
答案 1 :(得分:2)
这就是我做的。给定一个包含MetaInfo
列的表MetaKey varchar(max) and MeatValueLong bigInt
。
请注意,在我的情况下,目标是专门获得增加值而不重复。我使用行锁来在这个单独的操作上创建隔离。 (是的,我知道我可以使用插入和自动增量键,但是有一个额外要求,调用者可以传递最小值。)
CREATE PROCEDURE [dbo].[uspGetNextID]
(
@inID bigInt
)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION
-- This section can be removed if you want to pass in an id.
SET @inID = 0
UPDATE MetaInfo WITH (ROWLOCK)
SET MetaValueLong = CASE
WHEN ISNULL(MetaValueLong,0) > @inID THEN MetaValueLong+1
ELSE @inID+1
END
WHERE MetaKey = 'Internal-ID-Last'
SELECT MetaValueLong
FROM MetaInfo
WHERE MetaKey = 'Internal-ID-Last'
COMMIT TRANSACTION
END
答案 2 :(得分:1)
是的,SET ISOLATION LEVEL SERIALIZABLE正是您所需要的。它不允许脏写和脏读。可序列化事务内的所有db-objets都被锁定,因此只有当第一个连接提交或回滚时,其他连接才能读/写。