a";柜台;SQL Server中的表

a";柜台;SQL Server中的表,sql,sql-server,Sql,Sql Server,我正在使用一个大型SQL Server数据库,它基于主键值的计数器表的思想。每个表在此计数器表中都有一行,其中包含PK名称和下一个用作主键的值(对于该表)。我们当前获取计数器值的方法如下: BEGIN TRAN UPDATE CounterValue + 1 SELECT Counter Value COMMIT TRAN 这在很大程度上是有效的,因为启动事务,然后更新行的过程会锁定行/页/表(对于本主题来说,锁定级别并不太重要),直到提交事务为止 这里的问题是,如果事务长时间处于打开

我正在使用一个大型SQL Server数据库,它基于主键值的计数器表的思想。每个表在此计数器表中都有一行,其中包含PK名称和下一个用作主键的值(对于该表)。我们当前获取计数器值的方法如下:

BEGIN TRAN 
UPDATE CounterValue + 1 
SELECT Counter Value 
COMMIT TRAN 
这在很大程度上是有效的,因为启动事务,然后更新行的过程会锁定行/页/表(对于本主题来说,锁定级别并不太重要),直到提交事务为止

这里的问题是,如果事务长时间处于打开状态,则对该表/页/行的访问被锁定的时间过长。在某些情况下,单个事务中可能会发生数百次插入(需要访问此计数器表)

解决此问题的一种尝试是始终使用与应用程序分离的连接,该连接永远不会保持事务打开。访问表和事务的速度很快,因此通常可以访问表。这里的问题是,使用可能还需要访问这些计数器值的触发器,这使得这是一个相当不合理的规则。换句话说,我们有一些触发器也需要计数器值,这些触发器有时会在更大的父事务上下文中运行

解决此问题的另一种尝试是使用SQL Server应用程序锁来序列化对表/行的访问。大多数时候这也没问题,但也有缺点。这里最大的缺点之一还涉及触发器。由于触发器在触发查询的上下文中运行,因此应用程序锁将被锁定,直到任何父事务完成

因此,我试图找出一种序列化对行/表的访问的方法,该行/表可以从应用程序运行,也可以从永远不会在父事务上下文中运行的SP/触发器运行。如果父事务将回滚,我不需要计数器值来回滚。由于始终可用,快速访问计数器值比在回滚父事务时丢失几个计数器值要重要得多

我应该指出,我完全意识到使用GUID值或标识列可以解决我的许多问题,但正如我所提到的,我们谈论的是一个庞大的系统,拥有大量的数据,这些数据无法在合理的时间范围内更改,而不会给我们的客户带来很大的痛苦(我们谈论的是成百上千万行的数百张桌子)

任何关于实现这种计数器表的最佳方法的想法都将不胜感激。请记住,从许多应用程序、服务、触发器和其他SP都应该始终可以访问,并且几乎没有阻塞


编辑-我们可以假设SQL Server 2005+

系统目前的工作方式无法扩展。您已经注意到了这一点。以下是一些按偏好大致排列的解决方案:

  • 使用
    IDENTITY
    列(您可以设置
    IDENTITY
    属性,而无需重建表。搜索web以了解如何设置。)
  • 使用序列
  • 使用Hi-Lo ID generation()。简而言之,ID(应用程序实例)的使用者在单独的事务中检查大范围的ID(如100)。该方案的开销非常低

  • 使用下面评论中的约束条件:即使只有一个事务,并且没有应用程序级别的更改,也可以实现可伸缩的计数器生成。这是一种万不得已的措施

    条带化计数器。对于每个表,您有100个计数器。计数器
    N
    跟踪符合
    ID%100=N
    的ID。因此每个计数器跟踪所有ID的1/100

    当您想要获取ID时,可以从随机选择的计数器获取。并发事务不使用此计数器的可能性很大。由于SQL Server中的行级锁定,您将几乎没有阻塞

    您将计数器
    N
    初始化为
    N
    并将其递增100。这确保所有计数器生成不同的ID范围

    计数器
    0
    生成
    0、100、200、
    。计数器
    1
    生成
    1、101、201、
    。依此类推。 这样做的一个缺点是您的ID现在不是连续的。在我看来,应用程序无论如何都不应该依赖它,因为它不是一个可靠的属性


    您可以将所有这些抽象为一个过程调用。代码复杂性实际上不会太大。您基本上只需生成一个额外的随机数并更改递增逻辑。

    一种方法是在一条语句中获取并递增计数器值:

    DECLARE @NextKey int
    
    UPDATE Counter
    SET @NextKey = NextKey + 1,
        NextKey = @NextKey
    

    您使用的是哪个版本的SQL Server?SQL Server 2012终于支持序列了。我敢肯定,它比引擎之外构建的任何东西都更健壮、更快。因此,如果您不使用SQL Server 2012,升级可能比实现和维护国产解决方案更便宜。您仍然可以使用不带c的标识列挂起其他表。类似于
    创建表计数器(计数器INT标识(1,1)主键);
    ,然后
    插入计数器默认值;选择@@SCOPE\u IDENTITY;从计数器中删除,其中counter=@@SCOPE\u IDENTITY;
    ,也许?@Gordon Linoff-SQL Server 2005+。更新了我的OP.@hvd-我能看到改变我的情况的唯一方法是为我的每个数据表都有这样一个计数器表。否则,我将精简k我又回到了事务阻止访问它的情况。除非我可能不理解你的建议。另外,@@SCOPE_IDENTITY在2005年不受支持。是的,我完全同意我们当前的方案不能很好地扩展!1.标识列将是一个问题,不仅对于转换数据,而且对于应用程序级别l这不是我们所期望的,这需要我们改变