數據庫對並發的處理-樂觀鎖與悲觀鎖


假如兩個線程同時修改數據庫同一條記錄,就會導致后一條記錄覆蓋前一條,從而引發一些問題。

例如:

  一個售票系統有一個余票數,客戶端每調用一次出票方法,余票數就減一。

情景: 

  總共300張票,假設兩個售票點,恰好在同一時間出票,它們做的操作都是先查詢余票數,然后減一。

一般的sql語句:

  

declare @count as int
 
begin tran
    select @count=count from ttt
    WAITFOR DELAY '00:00:05' --模擬並發,故意延遲5秒
    update ttt set count=@count-1
commit TRAN
 
SELECT * FROM ttt

 

  問題就在於,同一時間獲取的余票都為300,每個售票點都做了一次更新為299的操作,導致余票少了1,而實際出了兩張票。

  打開兩個查詢窗口,分別快速運行以上代碼即可看到效果。

 

定義解釋:

  悲觀鎖:相信並發是絕大部分的,並且每一個線程都必須要達到目的的。

  樂觀鎖:相信並發是極少數的,假設運氣不好遇到了,就放棄並返回信息告訴它再次嘗試。因為它是極少數發生的。

 

悲觀鎖解決方案:

  

declare @count as int
 
begin tran
    select @count=count from tb WITH(UPDLOCK)
   WAITFOR DELAY '00:00:05' --模擬並發,故意延遲5秒
    update tb set count=@count-1
commit tran

 

  在查詢的時候加了一個更新鎖,保證自查詢起直到事務結束不會被其他事務讀取修改,避免產生臟數據。

  從而可以解決上述問題。

 

樂觀鎖解決方案:

 

--首先給表加一列timestamp
 
ALTER TABLE ttt ADD timesFlag TIMESTAMP NOT null
 
然后更新時判斷這個值是否被修改
declare @count as int
DECLARE @flag AS TIMESTAMP
DECLARE @rowCount AS int
begin tran
    select @count=COUNT,@flag=timesflag from ttt
    WAITFOR DELAY '00:00:05'
    update ttt set count=@count-1 WHERE timesflag=@flag --這里加了條件
    SET @rowcount=@@ROWCOUNT  --獲取被修改的行數
commit TRAN
 
--對行數進行判斷即可
 
IF @rowCount=1
    PRINT '更新成功'
ELSE
    PRINT '更新失敗'

 

  這便是樂觀鎖的解決方案,可以解決並發帶來的數據錯誤問題,但不保證每一次調用更新都成功,可能會返回'更新失敗'

 

悲觀鎖和樂觀鎖

  悲觀鎖一定成功,但在並發量特別大的時候會造成很長堵塞甚至超時,僅適合小並發的情況。

  樂觀鎖不一定每次都修改成功,但能充分利用系統的並發處理機制,在大並發量的時候效率要高很多。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM