SQL Server中“计数器”表的最佳实现

时间:2014-02-05 19:30:34

标签: sql sql-server

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

BEGIN TRAN 
UPDATE CounterValue + 1 
SELECT Counter Value 
COMMIT TRAN 

自从启动事务,然后更新行,锁定行/页/表(锁定级别对于此主题不太重要)之后,这种方法很有效,直到提交事务为止。

这里的问题是,如果一个事务长时间保持打开状态,那么对该表/页面/行的访问将被锁定太长时间。我们遇到的情况是,单个事务中可能会发生数百个插入(需要访问此计数器表)。

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

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

所以我想弄清楚的是一种序列化对行/表的访问的方法,该行/表可以从应用程序运行,也可以从永远不会在父事务的上下文中运行的SP /触发器运行。如果父事务将回滚,我不需要回滚计数器值。如果父事务被回滚,那么一直可用,快速访问计数器值比丢失一些计数器值更重要。

我应该指出,我完全意识到使用GUID值或标识列可以解决我的很多问题,但正如我所提到的,我们谈论的是一个庞大的系统,大量的数据不能在合理的时间框架内进行更改,而不会给客户带来太多痛苦(我们正在谈论数百个拥有数亿行的表格)。

任何有关实施此类计数器表的最佳方法的想法都将受到赞赏。请记住 - 访问应始终可以从许多应用程序,服务,触发器和其他SP中获得,几乎没有阻止。

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

2 个答案:

答案 0 :(得分:0)

系统目前在不可扩展的方式下工作的方式。你已经注意到了自己。以下是一些粗略优先顺序的解决方案:

  1. 使用IDENTITY列(您可以设置IDENTITY属性而无需重建表格。搜索网络以了解具体方法。)
  2. 使用序列
  3. 使用Hi-Lo ID生成(What's the Hi/Lo algorithm?)。简而言之,ID(应用程序实例)的消费者在单独的事务中检查大范围的ID(如100)。该计划的开销非常低。

  4. 使用下面评论中的约束:即使只有一个事务而且没有应用程序级别的更改,您也可以实现可扩展的计数器生成。这是一种不得已的措施。

    条纹计数器。对于每个表,您有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现在不是顺序的。在我看来,申请不应该依赖于此,因为它不是一个可靠的财产。

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

答案 1 :(得分:0)

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

DECLARE @NextKey int

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