并发环境中的SQL编号生成(转换隔离级别)

时间:2015-07-16 13:05:03

标签: sql sql-server tsql transactions

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

我现在正致力于该问题的解决方案,到目前为止,我已经到了我有一个存储过程执行插入的点,在插入之后,我有一个带有隔离级别可序列化的事务(由方式仅适用于该交易,还是应该在提交交易后将其设置回来?):

  • 获取数字
  • 如果没有找到,那么插入该表并且如果找到则更新数字(增量)
  • 提交交易

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

我读到了关于sp_getapplock的内容,这对实现我的目标有点好吗?

2 个答案:

答案 0 :(得分: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相同的防范竞争条件,但出于某种原因,我不记得证据在哪里。

答案 1 :(得分:0)

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

试试这个:

  1. 在tran 1中取一个新数字。
  2. 在tran 2中插入已经拍摄的号码
  3. 这样你可以刻录一个数字,但永远不会有不一致的数据。