ALTER PROC [dbo].[setRechargeSuccess]
(
@id INT,
@pay INT
)
AS
DECLARE @userId INT
DECLARE @status INT
--判断订单处
SELECT @status=status,@userId=userId FROM Recharge WHERE id=@id
IF(@status=1)
RETURN 65;--已处理了
ELSE
BEGIN
set xact_abort ON
BEGIN TRAN
UPDATE Recharge SET status=1 WHERE id=@id
UPDATE Users SET balance=balance+@pay WHERE ID=@userId
COMMIT TRAN
RETURN 66;--处理成功
END
我在想万一2个进程同时走到--判断订单处,结果查出来@status都是0,那账户的钱可能就会加两次了。有朋友说把表或者存储过程锁住,可是我的意思是比如第一个进程id=1,我只希望同时进来的哪个进程id也是1,我才让他在判断订单处等,如果同时进来的id=2,那没关系不用等,两个一起往下走,因为相互不影响。能否给出代码,效率高点的,只在相同id的进程等待,因为我发现好像真的有几率连加2次。万分感谢!!
------解决方案--------------------
没用的忘记去掉了-_-
ALTER PROC [dbo].[setRechargeSuccess]
(
@id INT,
@pay INT
)
AS
DECLARE @return_code INT
BEGIN
set xact_abort ON
BEGIN TRAN
UPDATE Recharge SET status=1 WHERE id=@id and status<>1
if @@rowcount>0 BEGIN
UPDATE Users SET balance=balance+@pay WHERE ID=(SELECT userId FROM Recharge WHERE id=@id)
SET @return_code=66
END
ELSE SET @return_code=65
COMMIT TRAN
RETURN @return_code;--处理成功
END
------解决方案--------------------
支持楼上的脚本!
------解决方案--------------------
UPDATE Recharge SET status=1 WHERE id=@id and status<>1
update语句在默认的事务级别中会加行锁,这期间会阻塞其他相同id的进程
也就是其他进程需要此进程事务提交后才能继续运行
那么假设当status=1时,没有数据可以更新,@@rowcount获得0行,进入else判断就是你最开始的那个65的判断
如果当status<>1时,update这里修改状态为1,然后进入if判断
假设有另一个相同id事务进行操作,当本次事务提交后,另一个进程才能继续update操作
这时status已经被修改为1了,所以后续的@@rowcount判断一定是0,
如此就避免了你上面写的查询赋值导致获得的状态值脏读的情况