如何找到我无法重现我在生产数据库中观察到的数据库更新竞赛的原因?

时间:2014-01-10 10:48:28

标签: sql concurrency transactions azure-sql-database race-condition

我有一个SQL Azure数据库,它运行的代码或多或少与此相同:

declare @magic int;
begin tran 
   select @magic=Magic from MagicTable where MagicId=SomeMagicValue;
   update MagicTable set Magic=Magic+SomeValue where MagicId=SomeMagicValue;
commit tran

我确信有时候,如果此代码在具有相同值SomrMagicValue的两个并发事务中运行,则会发生竞争,并且MagicMagicId中的值会被获取没有增加 - 显然两个事务开始并读取相同的值然后递增相同的值,因此其中一个事务会覆盖另一个事务的结果。

所以我决定添加一些锁定提示。为了验证我的更改,我想首先在测试数据库上重现该问题。所以我创建了一个具有相同模式的表,在那里添加了一些数据并运行以下代码:

declare @counter int;
set @counter=0;
begin tran
   while @counter < 10000
   begin
       declare @magic int;
       select @magic=Magic from MagicTable where MagicId=SomeMagicValue;
       update MagicTable set Magic=Magic+SomeValue where MagicId=SomeMagicValue;
       set @counter = @counter + 1;
   end
commit tran

并且此代码在Azure管理门户内部的两个并发事务中运行,每次运行需要几十秒而且从未出现过竞争 - 我的问题没有被重现。

我还尝试了以下变体:

declare @counter int;
set @counter=0;
while @counter < 10000
begin
    begin tran
       declare @magic int;
       select @magic=Magic from MagicTable where MagicId=SomeMagicValue;
       update MagicTable set Magic=Magic+SomeValue where MagicId=SomeMagicValue;
    commit tran
    set @counter = @counter + 1;
end

并且再也没有比赛 - 结果与交易不会并发的结果相同。

问题是 - 我如何找到生产环境中存在问题的原因而不是测试问题?

1 个答案:

答案 0 :(得分:0)

begin tran 
   update MagicTable set Magic=Magic+SomeValue where MagicId=SomeMagicValue;
commit tran

您在第一个查询中的选择是多余的;你从不使用@Magic而你实际上并不需要它。如果你这样做,交易应该处理竞争条件。谁知道?它可能不是一场比赛,而是你的代码中调用DB的错误......