一张数据库表面对大量请求的最佳解决方案

7
我们有一个系统,客户将按照先来先服务的原则分配产品。我们的产品表包含了一个从零开始的自增主键,用于追踪已分配多少产品,即用户预订产品时得到1个,下一个用户得到2个,以此类推。
问题在于,在任何给定的小时内,可能会有数十万用户访问系统,全部都会访问这个表。
由于我们需要确保每个客户只分配一个产品并追踪已分配多少产品,因此我们为访问系统的每个客户使用一行锁来确保他们在下一个客户访问系统之前写入表 - 即强制执行先到先得的规则。
我们担心的是每个请求进入 SQL Server 2008 Enterprise Edition 和行锁定的处理时间成为瓶颈。
我们不能使用多个服务器,因为我们需要确保主键的完整性,所以任何需要复制的东西都不会起作用。
是否有人知道任何有效地处理大量请求的数据库表的好方法?
更多信息:
问题中的表实际上仅包含两个字段 - ID 和 CustomerID。该解决方案是为100万件产品进行免费赠品 - 因此对高需求的期望以及为什么使用递增的主键作为关键字对我们很有意义 - 一旦关键字达到100万,就没有更多的客户可以注册。此外,产品都是不同的,因此分配正确的关键字非常重要,例如首先输入的100个客户会收到比下一个100个客户更高价值的产品等。

显而易见的第一个问题是:您是否已经剥离了获取密钥的交易中的其他所有内容?例如,在尝试获取密钥之前,您是否已经知道了需要知道的所有信息,然后去查找表格获取密钥并在执行其他操作之前进行解锁? - Karl
嗨,卡尔,是的,没错。其他所有东西都被剥离了 - 使事务尽可能轻量化 - 只需获取客户ID,然后进行插入并向用户显示新的主键,如“恭喜您,您是客户xxxxx,您将获得免费奖品”。 - Cais Manai
1个回答

6
首先,为了解决密钥生成的问题,我会提前生成所有密钥。这只涉及到100万行数据,这意味着您不必担心管理密钥生成过程。这也意味着您不必担心意外生成太多行,因为一旦填满表格,您只需要执行UPDATE操作,而不是INSERT操作。
这里有一个重要的问题,那就是这100万个项目是否完全相同?如果是的话,那么密钥的顺序无所谓(甚至可以没有顺序),因此当客户提交请求时,您只需尝试更新表格,大致如下:
UPDATE TOP(1) dbo.Giveaway -- you can use OUTPUT to return the key value here
SET CustomerID = @CurrentCustomerID
WHERE CustomerID IS NULL

IF @@ROWCOUNT = 0 -- no free items left
PRINT 'Bad luck'
ELSE
PRINT 'Winner'

如果另外的1m个物品是不同的,你需要另一种解决方案,例如第1个物品是X,第2-10个是Y,第11-50个是Z等。在这种情况下,按请求提交的顺序为客户分配密钥非常重要,因此您可能需要考虑使用某种排队系统,例如使用Service Broker。每个客户都向队列添加请求,然后存储过程逐个处理它们并将它们分配给最大空闲密钥,然后返回他们所获得的详细信息。

在SQL 2008中,SET ROWCOUNT已被弃用。是否考虑使用UPDATE TOP(1)代替?请参考http://msdn.microsoft.com/en-us/library/ms188774%28v=sql.100%29.aspx。 - Ed Harper
@EdHarper 是的,这是一个很好的观点,我已经更新了我的示例。尽管对于提问者来说,关键信息当然是只要更新一行就可以,无论更新哪一行都没关系。 - Pondlife
谢谢Pondlife。这些项目是不同的,所以听起来排队是正确的选择,并且在高峰期接受每个请求处理会有一些延迟。我想到的另一个解决方案是将所有奇数放在一个数据库中,偶数放在另一个数据库中,然后使用带有简单位开关的负载均衡器将交替的客户发送到数据库-对此有何看法?我猜我可能会将瓶颈移动到负载均衡器上,这可能最终也需要一个队列?感谢您的帮助,非常感激。 - Cais Manai
@CaisManai 我不会将数据分割,因为这样你就有太多的变量来确保系统按照你的期望处理数据。我想这取决于你想要付出多少努力来确保请求被“按顺序”处理,尽管在多层应用程序中,“按顺序”实际上意味着什么是一个有趣的问题。但我怀疑出于法律原因,你需要(至少在理论上)能够向审计员展示你“公平”和“按顺序”地处理了请求,因此我建议保持你的解决方案尽可能简单。 - Pondlife

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接