快钱支付与Sql Server的乐观锁和悲观锁
在實際的多用戶并發訪問的生產環境里邊,我們經常要盡可能的保持數據的一致性。而其中最典型的例子就是我們從表里邊讀取數據,檢查驗證后對數據進行修改,然后寫回到數據庫中。在讀取和寫入的過程中,如果在多用戶并發的環境里邊,其他用戶已經把你要修改的數據進行了修改是非常有可能發生的情況,這樣就造成了數據的不一致性。
最近在做快錢支付的時候就碰到了這個問題,原來的代碼如下:
1. 表Order的結構:
??? OrderId?? int 自增長
??? Status?? nvarchar(10)? //未處理時的狀態為"wait"
2. 相關SQL語句:
Select Status from order where OrderID= @OrderID
Update Order set Status?= 'Y' where OrderID=@OrderID
3.程式偽代碼:
var?status??=?GetOrderStatus(orderid); //獲取用戶充值狀態
if(status=?"wait")//如果狀態為未處理
???UpdateOrderStatus(orderid);//則更新狀態為已處理
???//后臺給用戶充值的代碼
按道理這樣的代碼是沒有問題的,因為對同一個用戶而已,并不存在并發的問題,也就不存在一次付款兩次充值的問題。
然而快錢的處理方式是用戶通過付款后,快錢要重新轉到我們的網站來,我們在收到快錢支付成功的請求后,給用戶充值,并將再次定向的頁面返回給快錢,快錢再定向到支付成功的頁面。
流程如下:用戶--->GoToPay.aspx-->快錢-->AfterPay.aspx-->快錢-->AfterPayMessage.aspx。
由于快錢使用的是輪循的機制,會每隔一秒鐘就訪問AfterPay.aspx,因此會多次訪問AfterPay.aspx,這時問題出來了:
var?status??=?GetOrderStatus(orderid); //獲取用戶充值狀態
if(status=?"wait")//如果狀態為未處理UpdateOrderStatus(orderid);//則更新狀態為已處理??//后臺給用戶充值的代碼,會充值兩次
在程式還未對Status更新的時候,第二次請求已經到達,這時使用GetOrderStatus,得到的還是"wait",因此會充值兩次。
解決方案:
方式一:
使用常規的樂觀鎖方案
表Order里邊加上一列TimeStamp 列,該列是varbinary(8)類型。但是在更新的時候這個值會自動增長。?
Select Status,TimeStamp from order where OrderID= @OrderID-- 更新狀態,但是要比較時間戳是否發生了變化.如果沒有發生變化,影響行數為1,更新成功.如果發生變化,影響行數為0。
update Order
set Status="Y",
where OrderID=@OrderID and TimeStamp=@timestamp
set @rowcount=@@rowcount程式的修改
var?status??=?GetOrderStatus(orderid,out timestamp); //獲取用戶充值狀態
if(status=?"wait")//如果狀態為未處理
???int rowcount =?UpdateOrderStatus(orderid,timestamp);
if(rowcount=?1)?//狀態未更新
? 充值
else //快錢第二次過來的時候,返回行數為0
?return? ?"已經充過值"
endif
方式二:
還是樂觀鎖方案:
由于表Order的Status本身就可以起到跟timestamp列一樣的效果,修改如下:update Order
set Status="Y",
where OrderID=@OrderID and Status="wait"
set @rowcount=@@rowcount程式的修改var?status??=?GetOrderStatus(orderid); //獲取用戶充值狀態
if(status=?"wait")//如果狀態為未處理
???int rowcount =?UpdateOrderStatus(orderid);
if(rowcount=?1)?//狀態未更新
? 充值
else //快錢第二次過來的時候,返回行數為0
?return? ?"已經充過值"
endif
此方案更為簡單。
方案三:
使用悲觀鎖的方式,這次修改的SQL語句不是Update 而是Select
如下:
Select Status???from order?? ?with (UPDLOCK) where OrderID= @OrderID
程式完全不用修改:
//獲取用戶充值狀態,快錢第二次過來的時候,如果第一次還未更新,則該訂單行還處于鎖定狀態,因此會等待第一次更新完以將鎖釋放
var?status??=?GetOrderStatus(orderid);?
if(status=?"wait")//如果狀態為未處理
???UpdateOrderStatus(orderid);//則更新狀態為已處理
???//后臺給用戶充值的代碼
這種方式最簡單,程式完全不用修改,只需要在存儲過程中加上with (UPDLOCK)即可。
缺點是對大量的并發性能會很差,而且會引起死鎖。當然對于充值這種交易而言,還是可以比較適合的。
這是我寫出來的第一篇技術類的文章,可能很多地方講得不夠透徹,希望大家指正。
下一篇文章我想就航空公司售票導致的并發問題提供一個更簡單的解決方案。
總結
以上是生活随笔為你收集整理的快钱支付与Sql Server的乐观锁和悲观锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 定期存款到期日当天可以取吗
- 下一篇: 外汇市场和股票市场存在三个不同,一般人不