日期:2014-05-17  浏览次数:20589 次

急啊,跪求大侠指点,怎么保证这条支付记录的可靠性

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,
如此就避免了你上面写的查询赋值导致获得的状态值脏读的情况