最近悟出來一個道理,在這兒分享給大家:學歷代表你的過去,能力代表你的現在,學習代表你的將來。
十年河東十年河西,莫欺少年窮
學無止境,精益求精
之前我的博客:SQL-樂觀鎖,悲觀鎖之於並發詳細介紹了樂觀鎖悲觀鎖的應用,在此,通過程序來驗證:
首先,打開SQLserver,新建一張表:
create table Ticket ( Id int identity(1,1) primary key, ticketCount int , )
insert into Ticket values(300)
我們首先示范悲觀鎖:如果您還不太明白悲觀鎖的原理,請查閱我的上篇博客SQL-樂觀鎖,悲觀鎖之於並發
悲觀鎖在使用過程中會鎖住要操作的對象,其他用戶可以查詢該對象的內容,但是不能更新該對象,直至悲觀鎖釋放資源。因此,悲觀鎖以嚴格的控制並發,從而使並發零發生。
請在SQLserver中打開兩個查詢窗口,並輸入如下語句:
declare @count as int begin tran select @count=ticketCount from Ticket with(updlock) waitfor delay '00:00:05' update Ticket set ticketCount=@count-1 commit tran select * from Ticket
上述語句中的waitfor delay '00:00:05' 代表延遲五秒,用於模擬並發。with(updlock)代表查詢時,上了悲觀鎖,一旦上了悲觀鎖,其他用戶必須等待悲觀鎖釋放后才能訪問被鎖定的對象,否則,處於等待狀態!
快速執行每個窗口中的語句:
執行結果如下:


從上圖可以看出,執行二次的結果票數減少2,執行結果正確
要說明的是,在窗口2執行時,必須要等到窗口1執行結束,如果窗體1還在執行,則窗體2處於等待狀態。
為了驗證悲觀鎖發生了作用,我們將悲觀鎖拿掉,然后再快速執行二個窗體中的語句<將票數重置為300,方便我們測試>:
拿掉悲觀鎖后的語句變更為:
declare @count as int begin tran select @count=ticketCount from Ticket waitfor delay '00:00:05' update Ticket set ticketCount=@count-1 commit tran select * from Ticket
快速執行二個窗體語句,執行結果如下:

由圖可知發生了並發,原因是出現了臟讀<表中的實際值是299,出了二張票,而票數卻只減少1,這顯然是錯誤呀的>。
下面直接貼出樂觀鎖代碼:
修改表結構:添加一列,數據類型為時間戳
Alter Table Ticket Add TimeFlag TimeStamp not null
然后修改事務如下:
declare @count as int declare @flag as TimeStamp declare @rowcount As int begin tran select @count=ticketCount,@flag=TimeFlag from Ticket waitfor delay '00:00:05' update Ticket set ticketCount=@count-1 where TimeFlag=@flag set @rowcount=@@ROWCOUNT if @rowcount>0 print '更新成功' else print '更新失敗' commit tran select * from Ticket
窗體1執行圖


窗體2執行圖


由圖可知,窗體2執行失敗,是因為當窗體1執行后,時間戳會隨着時間變更變為一個新的值,而再次執行時,由於時間戳不同,造成執行失敗,從而避免了並發帶來的影響。
出了一張票,票數減少1,執行結果正確
為了使系統更加流暢,在窗體2執行失敗后,應再次讀取數據庫並重新執行!
@陳卧龍的博客
