现有一改变用户余额的存储过程,大致如下:
Create PROCEDURE [dbo].[proc_ChangeBalance]
@userid int,--用户编号
@changemoney decimal(18,2)--改变金额,正为收入,负为支出
AS
BEGIN
declare @curBalance decimal(18,2) = 0 --当前余额
declare @newBalance decimal(18,2) = 0 --改变后余额
declare @curAccountId int = -1--当前账户编号
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
-----------------------------------------开始事务----------------------------
begin transaction
save tran changeBalance
--获取当前账户信息
select @curAccountId = ID, @curBalance =ISNULL(Balance,0) from UserBalance with(rowlock) where UserId=@userid --余额不足
if(@changemoney < 0 and @curBalance < ABS(@changemoney))
begin
rollback transaction changeBalance
commit transaction
return
end

declare @error int = 0--错误信息

set @newBalance = @curBalance + @changemoney
--执行账户修改
update UserBalance with(rowlock) set Balance = @newBalance where ID=@curAccountId
set @error = @error + @@ERROR
--记录账户流水日志
Insert Into BalanceLog
values
(@userid,@curBalance,@changemoney,@newBalance,getdate())
set @error = @error + @@ERROR if(@error = 0)
begin
commit transaction
end
else
begin
rollback transaction changeBalance
commit transaction
end
END现在的问题是在某些并发的时候,两次执行取到的当前余额都是一样的,导致最后只有一个执行有效,如果没有生效那个执行是扣钱的话,那最终就没有扣钱,如果是加钱的话,就没有加钱了.怎么锁定这个执行,让第二次执行的时候始终等待第一个执行完成,这样第二次执行的时候取到的当前余额才是真实有效的.谢谢!

解决方案 »

  1.   

    SET TRANSACTION ISOLATION LEVEL READ COMMITTED
    改为:
    SET TRANSACTION ISOLATION LEVEL serializable
      

  2.   

    · 未提交读 在读数据时不会检查或使用任何锁。因此,在这种隔离级别中可能读取到没有提交的数据。 · 已提交读 只读取提交的数据并等待其他事务释放排他锁。读数据的共享锁在读操作完成后立即释放。已提交读是SQL Server的默认隔离级别。 · 可重复读 像已提交读级别那样读数据,但会保持共享锁直到事务结束。 · 可序列化 工作方式类似于可重复读。但它不仅会锁定受影响的数据,还会锁定这个范围。这就阻止了新数据插入查询所涉及的范围,这种情况可以导致幻像读。