標籤:blog ar for 資料 div 問題 代碼 sp log
假如兩個線程同時修改資料庫同一條記錄,就會導致後一條記錄覆蓋前一條,從而引發一些問題。
例如:
一個售票系統有一個餘票數,用戶端每調用一次出票方法,餘票數就減一。
情景:
總共300張票,假設兩個售票點,恰好在同一時間出票,它們做的操作都是先查詢餘票數,然後減一。
一般的sql語句:
declare @count as intbegin tran select @count=count from ttt WAITFOR DELAY ‘00:00:05‘ --類比並發,故意延遲5秒 update ttt set [email protected]commit TRANSELECT * FROM ttt
問題就在於,同一時間擷取的餘票都為300,每個售票點都做了一次更新為299的操作,導致餘票少了1,而實際出了兩張票。
開啟兩個查詢時段,分別快速運行以上代碼即可看到效果。
定義解釋:
悲觀鎖:相信並發是絕大部分的,並且每一個線程都必須要達到目的的。
樂觀鎖:相信並發是極少數的,假設運氣不好遇到了,就放棄並返回資訊告訴它再次嘗試。因為它是極少數發生的。
悲觀鎖解決方案:
declare @count as intbegin tran select @count=count from tb WITH(UPDLOCK) WAITFOR DELAY ‘00:00:05‘ --類比並發,故意延遲5秒 update tb set [email protected]commit tran
在查詢的時候加了一個更新鎖定,保證自查詢起直到事務結束不會被其他事務讀取修改,避免產生髒資料。
從而可以解決上述問題。
樂觀鎖解決方案:
--首先給表加一列timestampALTER TABLE ttt ADD timesFlag TIMESTAMP NOT null然後更新時判斷這個值是否被修改declare @count as intDECLARE @flag AS TIMESTAMPDECLARE @rowCount AS intbegin tran select @count=COUNT,@flag=timesflag from ttt WAITFOR DELAY ‘00:00:05‘ update ttt set [email protected] WHERE [email protected] --這裡加了條件 SET @[email protected]@ROWCOUNT --擷取被修改的行數commit TRAN--對行數進行判斷即可IF @rowCount=1PRINT ‘更新成功‘ELSEPRINT ‘更新失敗‘
這便是樂觀鎖的解決方案,可以解決並髮帶來的資料錯誤問題,但不保證每一次調用更新都成功,可能會返回‘更新失敗‘
悲觀鎖和樂觀鎖
悲觀鎖一定成功,但在並發量特別大的時候會造成很長堵塞甚至逾時,僅適合小並發的情況。
樂觀鎖不一定每次都修改成功,但能充分利用系統的並發處理機制,在大並發量的時候效率要高很多。
sql server對並發的處理-樂觀鎖和悲觀鎖