SQL Server - 实现序列

Bar*_*ast 5 t-sql sql-server sequence

我有一个系统,要求我在进入数据库之前对我的数据有ID.我使用的是GUID,但发现它们太大而不方便.

我现在正在尝试实现一个序列生成器,它基本上为给定的上下文保留了一系列唯一ID值.代码如下;

ALTER PROCEDURE [dbo].[Sequence.ReserveSequence]
@Name varchar(100),
@Count int,
@FirstValue bigint OUTPUT
AS
BEGIN
SET NOCOUNT ON;

-- Ensure the parameters are valid
IF (@Name IS NULL OR @Count IS NULL OR @Count < 0)
    RETURN -1;

-- Reserve the sequence
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION

    -- Get the sequence ID, and the last reserved value of the sequence
    DECLARE @SequenceID int;
    DECLARE @LastValue bigint;

    SELECT TOP 1 @SequenceID = [ID], @LastValue = [LastValue]
    FROM [dbo].[Sequences]
    WHERE [Name] = @Name;

    -- Ensure the sequence exists
    IF (@SequenceID IS NULL)
    BEGIN
        -- Create the new sequence
        INSERT INTO [dbo].[Sequences] ([Name], [LastValue])
        VALUES (@Name, @Count);

        -- The first reserved value of a sequence is 1
        SET @FirstValue = 1;
    END
    ELSE
    BEGIN
        -- Update the sequence
        UPDATE [dbo].[Sequences]
        SET [LastValue] = @LastValue + @Count
        WHERE [ID] = @SequenceID;

        -- The sequence start value will be the last previously reserved value + 1
        SET @FirstValue = @LastValue + 1;
    END

COMMIT TRANSACTION

END
Run Code Online (Sandbox Code Playgroud)

"序列"表只是一个ID,名称(唯一),以及序列的最后一个分配值.使用此过程,我可以在命名序列中请求N个值,并将它们用作我的标识符.

到目前为止这种方法效果很好 - 它非常快,因为我不必经常询问单个值,我可以使用一系列值然后请求更多.

问题是,在极高的频率下,同时调用该过程有时会导致死锁.我只是发现压力测试会发生这种情况,但我担心它会在生产中出现.这个过程中是否有任何明显的缺陷,任何人都可以推荐任何改进方法吗?例如,没有事务就可以了,但我确实需要这是'线程安全的'.

Bar*_*ast 0

我想我会分享我的解决方案。我不会死锁,也不会产生重复的值。这个过程和我原来的过程之间的一个重要区别是,如果队列尚不存在,它不会创建队列。

ALTER PROCEDURE [dbo].[ReserveSequence]
(
    @Name nvarchar(100),
    @Count int,
    @FirstValue bigint OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON;

    IF (@Count <= 0)
    BEGIN
        SET @FirstValue = NULL;
        RETURN -1;
    END

    DECLARE @Result TABLE ([LastValue] bigint)

    -- Update the sequence last value, and get the previous one 
    UPDATE [Sequences]
    SET [LastValue] = [LastValue] + @Count
    OUTPUT INSERTED.LastValue INTO @Result
    WHERE [Name] = @Name;

    -- Select the first value
    SELECT TOP 1 @FirstValue = [LastValue] + 1 FROM @Result;
END
Run Code Online (Sandbox Code Playgroud)