sql server對併發的處理-樂觀鎖和悲觀鎖

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

例如:數據庫

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

情景: 線程

  總共300張票,假設兩個售票點,剛好在同一時間出票,它們作的操做都是先查詢餘票數,而後減一。blog

通常的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,而實際出了兩張票。it

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

 

定義解釋:效率

  悲觀鎖:相信併發是絕大部分的,而且每個線程都必需要達到目的的。date

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

 

悲觀鎖解決方案:

  

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 '更新失敗'

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

 

悲觀鎖和樂觀鎖

  悲觀鎖必定成功,但在併發量特別大的時候會形成很長堵塞甚至超時,僅適合小併發的狀況。

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

相關文章
相關標籤/搜索