【问题标题】:SQL number generation in concurrent environment (Transation isolation level)并发环境下SQL数生成(事务隔离级别)
【发布时间】:2015-07-16 22:19:37
【问题描述】:

我正在使用一个生成发票编号的应用程序(按顺序基于几个参数),到目前为止,它一直在使用带有序列化交易的触发器。因为触发器相当“重”,它设法使插入查询的执行超时。

我现在正在研究解决该问题的方法,到目前为止,我已经到了我有一个执行插入的存储过程并且在插入之后我有一个隔离级别可序列化的事务(顺便说一句适用仅针对该事务,还是应该在事务提交后将其设置回来?):

  • 获取号码
  • 如果找不到,则插入该表,如果找到则更新数字(增量)
  • 提交事务

我想知道是否有更好的方法来确保数字被使用一次并在表格锁定的情况下获得增量(只有数字表格被锁定,对吗?)。

我阅读了有关 sp_getapplock 的信息,这会是实现我目标的更好方法吗?

【问题讨论】:

  • 我认为没有看到问题出现在代码中的位置,您将得到的最好结果就是在黑暗中开枪。
  • 如果序列号只有一个来源,请使用标识列。如果有多个源并且您的存储过程每次都选择正确的源,请使用sequences
  • @GSerg 看起来很有趣,我不是数据库专家,我根本不知道序列。从那个解决方案中我还需要一件事——我必须能够轻松地修改下一个序列号的编号。我能用序列做到这一点吗?
  • @GSerg 我不能使用序列,我必须让它与 SQL Server 2008 兼容。
  • @user1970395 目前还不清楚您的设置是什么。您如何计算数字,将它们存储在何处以及为何存储它们?

标签: sql sql-server tsql transactions


【解决方案1】:

我会优化更新例程(并单独处理“insert if not there”),此时它将是:

declare @number int;

update tbl
set @number = number, number += 1
where year = @year and month = @month and office = @office and type = @type;

您不需要任何特定的锁定提示或隔离级别,SQL Server 将确保没有两个事务在递增之前读取相同的值。


如果您不想单独处理插入,您可以:

merge into tbl
using (values (@year, @month, @office, @type)) as v(y,m,o,t)
on tbl.year = v.year and tbl.month = v.month and tbl.office = v.office and tbl.type = v.type
when not matched by target then
  insert (year, month, office, type, number) values(@year, @month, @office, @type, 1)
when matched then
  update set @number = tbl.number, tbl.number += 1
;

从逻辑上讲,这应该提供与 update 相同的竞争条件防护,但由于某种原因,我不记得证据在哪里。

【讨论】:

  • 谢谢@GSerg,我相信这应该可以解决问题,尽管我在问题中描述的那个按预期工作,没有问题和死锁(或超时),但您的第一个解决方案似乎更清晰!你确定它不需要交易吗? ;> 因为它在 SP 内部使用,它是一个批处理,而不是一个事务(除非 .NET 将它包装到后台事务中,我不确定)
  • 为了避免两个客户端生成相同的 id,这个特定的查询不需要事务。对于其他任何事情,只要您有多个应该一起工作的语句,您当然应该使用事务。
【解决方案2】:

如果您先插入然后更新,则会有一个时间窗口,其中设置了无效数字并且可以观察到。此外,如果第二笔交易失败(这种情况总是会发生),您的数据就会不一致。

试试这个:

  1. 在 tran 1 中取一个新数字。
  2. 在 tran 2 中插入已占用的号码

这样你可能会烧掉一个数字,但永远不会出现不一致的数据。

【讨论】:

  • 如果在同一个事务中插入和更新,只有对脏读满意的人才能看到,否则无效数字没有时间窗口。
  • @GSerg 我同意,建议的解决方案的缺点是我必须确保插入会发生,否则我可能会出现空白,并且根据任何国家/地区的金融法,我认为发票中必须没有空白数字。
  • @usr 并且我也不介意可能会观察到无效数字,插入和其余部分在大约 30 毫秒内执行,因此时间窗口相当无时间。
  • @user1970395 我是否理解正确,您现在正在使用两个交易?然后你有潜在的数据不一致。如果不允许不一致,则不能这样做。
猜你喜欢
  • 2011-09-30
  • 2014-11-18
  • 1970-01-01
  • 2015-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多