0

我在 SQL Server 中有这个触发器

ALTER TRIGGER [dbo].[myTrigger]     
ON [dbo].[Data]     
AFTER INSERT 
AS 
BEGIN 

declare @number int

begin transaction
  select top 1 @number = NextNumber FROM settings

  Update Settings
    set NextNumber = NextNumber + 1

  UPDATE Data
    set number = @nnumber, currentDate = GetDate(), IdUser = user_id(current_user)
  FROM Data
    INNER JOIN inserted on inserted.IdData = Data.IdData

commit transaction

END

它按预期工作,但我想知道当多个用户Data同时在表中添加新行时它是否会按预期工作?

4

2 回答 2

2

让我们分析一下这段代码:

begin transaction

您使用默认设置开始事务READCOMMITTED

select top 1 @number = NextNumber FROM settings

您正在从Settings表中选择最大的数字(顺便说一句:您应该无论如何添加一个ORDER BY子句- 否则,不能保证排序!您可能会在这里得到意想不到的结果)。

然而,这个操作不是阻塞的——两个或多个线程可以同时读取相同的值,例如 100——SELECT只需要一个非常短的时间段的共享锁,并且共享锁是兼容的——多个读取器可以读取同时值。

Update Settings
set NextNumber = NextNumber + 1

现在在这里,一个线程获得绿灯并将新值(在我们的示例中为 101)写回到表中。该表有一个UPDATE独占锁(后来升级为独占锁) - 只有一个线程可以同时写入

 UPDATE Data
 set number = @nnumber, currentDate = GetDate(), IdUser = user_id(current_user)
 FROM Data
 INNER JOIN inserted on inserted.IdData = Data.IdData

同样的事情 - 一个幸运的线程可以更新Data表,将数字设置为100并且它正在更新的表的行被锁定直到事务结束。

 commit transaction

现在,幸运线程提交了他的事务并完成了。

但是:读取相同原始值 100 的第二个(可能还有第三个、第四个、第五个......)线程仍然“在循环中” - 现在线程 #1 已经完成,这些线程中的第二个开始做它的事——它做了。它Settings正确地将表更新为新值 102,并继续对表进行第二次更新,这里也使用它已读入其变量Data的“当前”值....100@number

最后,您可能有多个线程都从Settings表中读取相同的原始值(100),并且每个线程都会将Settings表更新为相同的“新”值(101)。

您在这里使用的这种方法在 load 下是不安全的

可能的解决方案:

  1. 首先也是最重要的 -推荐的方法:让数据库自己处理,通过使用INT IDENTITY表中的列(或者如果您已经在使用 SQL Server 2012 - 使用SEQUENCE对象来处理所有同步)

  2. 如果你不能这样做——无论出于何种原因——那么至少要确保你的代码即使在繁忙的系统上也能正常工作!例如,当第一个线程到来并读取当前值时,您需要在表上SELECT .... WITH (UPDLOCK)放置一个(独占UPDATE锁——这将阻止所有其他线程甚至读取“当前”值,直到第一个线程完成。或者还有其他替代方法,例如在单个操作中更新和分配旧值。SettignsUPDATE

于 2012-07-31T15:27:09.090 回答
1

模拟多个用户执行存储过程

您可以在 SQL Server Management Studio 中使用两个(或更多)编辑窗口,并在每个窗口中同时执行类似的操作。

insert into Data(ColName) values ('Value')
go 10000

go 10000将执行批处理 10000 次。将其调整为您认为合适的任何值。

于 2012-07-31T18:51:22.730 回答