0

我们有带有标识列(OrderID)的订单表,但我们的订单号由 OrderType(2 个字符)、OrderYear(2 个字符)和 OrderID(6 个字符)组成,总共 10 个字符(即 XX12123456)。此计数器有局限性:我们可以将身份 999999 视为 OrderID 。下一个订单的 ID 由 7 个字符组成。显然我们不能重复订单 ID。

所以我们创建了一个预先填充了渐进式 OrderID 和 OrderYear 的表(例如,从 100000 到 999999,订单年从 12 到 16):这个存储过程开始一个具有 SERIALIZABLE 隔离级别的事务,取第一个未使用的订单 ID,将其更新为使用并提交事务。

作为我们的订单表,我担心执行订单 ID 计算存储过程或重复订单 ID 时出现死锁。

我将使用创建多个并发线程并尝试提取模拟生产负载的 orderid 的控制台应用程序对此进行测试。

疑点是:

  • 是否存在另一种安全模拟标识列的方法?
  • 可以考虑使用触发器吗?
  • 可以考虑不同的隔离级别吗?
  • 其他想法?:D

谢谢!

[编辑]

在谷歌搜索并阅读了一堆 MSDN 文档之后,我发现了许多示例,这些示例显示了如何管理错误和解除锁定以及直接从 SP 处理一种自动回复,如下所示:

创建过程 [dbo].[sp_Ordine_GetOrderID]

@AnnoOrdine AS NVARCHAR(2) = 空输出,@IdOrdine AS INT = 空输出

作为

设置无计数

声明 @retry 为 INT SET @retry = 2

设置事务隔离级别可序列化

WHILE (@retry > 0) BEGIN BEGIN TRY

    BEGIN TRANSACTION OrderID   

    SELECT TOP 1 @AnnoOrdine = AnnoOrdine, @IdOrdine = IdOrdine
    FROM ORDINI_PROGRESSIVI --WITH (ROWLOCK)
    WHERE Attivo = 1
    --ORDER BY AnnoOrdine ASC, IDOrdine ASC 

    UPDATE ORDINI_PROGRESSIVI WITH (UPDLOCK)
    SET Attivo = 0
    WHERE AnnoOrdine = @AnnoOrdine AND IdOrdine = @IdOrdine

    IF ISNULL(@IdOrdine, '') = '' OR ISNULL(@AnnoOrdine,'') = ''
    BEGIN
        RAISERROR('Deadlock', 1, 1205)
    END

    SET @retry = 0

    COMMIT TRANSACTION OrderID  

    SELECT @AnnoOrdine AS AnnoOrdine, @IdOrdine AS IdOrdine 

END TRY
BEGIN CATCH

    IF (ERROR_NUMBER() = 1205)
        SET @retry = @retry - 1;
    ELSE
        SET @retry = -1;

    IF XACT_STATE() <> 0
        ROLLBACK TRANSACTION;       

END CATCH

结尾

这种方法减少了死锁(根本不存在),但有时我得到了 EMPTY 输出参数。使用 30 个当代线程进行测试(因此,30 个同时插入订单的客户进程)

这是一个带有查询持续时间的调试日志,以毫秒为单位:http: //nopaste.info/285f558758.html

足够强大的生产能力?

4

1 回答 1

0

如果您确实发现当前的解决方案正在产生问题,并且可能不会,那么另一种方法是为您要创建的每个 id 类型创建一个表,其中包含一个标识列和一个虚拟字段

IE:

 ABtypeID (ABID int identity(1,1), dummy varchar(1))

然后,您可以将记录插入此表并使用内置函数来检索身份。

IE

insert ABTypeID (dummy) values (null)
select Scope_Identity()

您可以根据需要从这些表中删除,并在年底截断它们以重置 id 计数器。

您甚至可以将插入包装在回滚的事务中 - 身份值不受回滚的影响

于 2012-10-19T08:08:08.177 回答