4

我们即将将我们的网站移动到 Windows azure 并遇到 sql-azure 的问题。由于性能优化,它们不支持标识增量属性。因此,我们必须设计自己的逻辑来支持生成顺序标识的现有功能。

因此,为了生成唯一的顺序值,我们从表的 ID 字段中获取最大值(值)并将其递增 1 以插入新记录。

我们现在必须管理并发事务,因此检查隔离级别。但是,没有一个隔离级别会锁定表以避免在并发下读取最大值。

任何有关锁定表格的帮助或对此方法的评论都会非常有帮助。如果你们中的一些人已经以一种很好的方式克服了这个问题,那么你能分享一下吗?谢谢你的帮助。

4

3 回答 3

5

使用以下过程来增加存储在单独表中的值。在主表上设置排他锁会导致可怕的并发问题。

CREATE PROCEDURE [dbo].[GetNextID](
    @IDName nvarchar(255)
)
AS
BEGIN
    /*
        Description:    Increments and returns the LastID value from tblIDs for a given IDName
        Author:         Max Vernon / Mike Defehr
        Date:           2012-07-19

    */

    DECLARE @Retry int;
    DECLARE @EN int, @ES int, @ET int;
    SET @Retry = 5;
    DECLARE @NewID int;
    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
    SET NOCOUNT ON;
    WHILE @Retry > 0
    BEGIN
        BEGIN TRY
            UPDATE dbo.tblIDs 
            SET @NewID = LastID = LastID + 1 
            WHERE IDName = @IDName;

            IF @NewID IS NULL
            BEGIN
                SET @NewID = 1;
                INSERT INTO tblIDs (IDName, LastID) VALUES (@IDName, @NewID);
            END
            SET @Retry = -2; /* no need to retry since the operation completed */
        END TRY
        BEGIN CATCH
            IF (ERROR_NUMBER() = 1205) /* DEADLOCK */
                SET @Retry = @Retry - 1;
            ELSE
                BEGIN
                SET @Retry = -1;
                SET @EN = ERROR_NUMBER();
                SET @ES = ERROR_SEVERITY();
                SET @ET = ERROR_STATE()
                RAISERROR (@EN,@ES,@ET);
                END
        END CATCH
    END
    IF @Retry = 0 /* must have deadlock'd 5 times. */
    BEGIN
        SET @EN = 1205;
        SET @ES = 13;
        SET @ET = 1
        RAISERROR (@EN,@ES,@ET);
    END
    ELSE
        SELECT @NewID AS NewID;
END
GO

(为了完整起见,这里是与存储过程关联的表)

CREATE TABLE [dbo].[tblIDs]
(
    IDName nvarchar(255) NOT NULL,
    LastID int NULL,
    CONSTRAINT [PK_tblIDs] PRIMARY KEY CLUSTERED 
    (
        [IDName] ASC
    ) WITH 
    (
        PAD_INDEX = OFF
        , STATISTICS_NORECOMPUTE = OFF
        , IGNORE_DUP_KEY = OFF
        , ALLOW_ROW_LOCKS = ON
        , ALLOW_PAGE_LOCKS = ON
        , FILLFACTOR = 100
    ) 
);
GO

每次你想获得一个新的 ID 用于主表时,你只需EXEC GetNextID 'TableIDField';

于 2013-11-08T05:48:42.820 回答
1

你可以通过这种方式实现类似的东西

  1. 启动一个事务(在读提交事务隔离级别)。

  2. SELECT在查询((XLOCK)提示)中选择具有排他锁的最大现有值。

  3. 更新要增加 1 的值。

  4. 提交事务。

将排他锁放入 select 语句将同时锁定所有其他想要读取 ID 的进程。他们将不得不等到事务完成,如果事务已提交,他们将读取新值。


正如下面 Max Vernon 所指出的,这种方法可能不适合大容量、高并发的系统。它也可能导致死锁(尽管死锁的可能性不限于此解决方案)。

于 2013-11-08T05:09:05.253 回答
0

** 不是 Sql 答案 ** 在过去,我通过将 ID 传递到插入中解决了类似的情况。

为了确保它是正确的 id,您需要公开一个服务,该服务在一个盒子上运行,该盒子只从内存中返回一个整数,该整数在每次请求后自动递增。

如果服务停止,请从您的表中获取最大 id,增加并再次开始分发 Id。

这与队列非常相似。

于 2014-02-25T00:18:25.647 回答