詳解樂觀鎖、悲觀鎖以及它們各自的應用

為什麼需要鎖(併發控制)?

在多用戶環境中,在同一時間可能會有多個用戶更新相同的記錄,這會產生衝突。這就是著名的併發性問題。

典型的衝突有:

l 丟失更新:一個事務的更新覆蓋了其它事務的更新結果,就是所謂的更新丟失。例如:用戶A把值從6改為2,用戶B把值從2改為6,則用戶A丟失了他的更新。

l 髒讀:當一個事務讀取其它完成一半事務的記錄時,就會發生髒讀取。例如:用戶A,B看到的值都是6,用戶B把值改為2,用戶A讀到的值仍為6。

為了解決這些併發帶來的問題,需要引入併發控制機制。


併發控制機制

悲觀鎖:假定會發生併發衝突,屏蔽一切可能違反數據完整性的操作。

樂觀鎖:假設不會發生併發衝突,

只在提交操作時檢查是否違反數據完整性。 樂觀鎖不能解決髒讀的問題。


樂觀鎖應用

總是認為不會產生併發問題,每次去取數據的時候總認為不會有其他線程對數據進行修改,因此不會上鎖,但是在更新時會判斷其他線程在這之前有沒有對數據進行修改,一般會使用版本號機制或CAS操作實現。

version方式:一般是在數據表中加上一個數據版本號version字段,表示數據被修改的次數,當數據被修改時,version值會加一。當線程A要更新數據值時,在讀取數據的同時也會讀取version值,在提交更新時,若剛才讀取到的version值為當前數據庫中的version值相等時才更新,否則重試更新操作,直到更新成功。

核心SQL代碼:

update table set x=x+1, version=version+1 where id=#{id} and version=#{version};

CAS操作方式:即compare and swap 或者 compare and set,涉及到三個操作數,數據所在的內存值,預期值,新值。當需要更新時,判斷當前內存值與之前取到的值是否相等,若相等,則用新值更新,若失敗則重試,一般情況下是一個自旋操作,即不斷的重試。


悲觀鎖應用

需要使用數據庫的鎖機制,比如SQL SERVER 的TABLOCKX(排它表鎖) 此選項被選中時,SQL Server 將在整個表上置排它鎖直至該命令或事務結束。這將防止其他進程讀取或修改表中的數據。在Java中,synchronized的思想也是悲觀鎖。

SqlServer中使用

Begin Tran
select top 1 @TrainNo=T_NO
from Train_ticket with (UPDLOCK) where S_Flag=0
update Train_ticket
set T_Name=user,
T_Time=getdate(),
S_Flag=1
where T_NO=@TrainNo
commit

我們在查詢的時候使用了with (UPDLOCK)選項,在查詢記錄的時候我們就對記錄加上了更新鎖,表示我們即將對此記錄進行更新. 注意更新鎖和共享鎖是不衝突的,也就是其他用戶還可以查詢此表的內容,但是和更新鎖和排它鎖是衝突的.所以其他的更新用戶就會阻塞.


實際上,在生產環境裡如果併發量不大且不允許髒讀,可以使用悲觀鎖解決併發問題;但如果系統的併發非常大的話,悲觀鎖定會帶來非常大的性能問題,所以我們就要選擇樂觀鎖定的方法。大家可以通過這個來判定選擇什麼鎖。

後期會分享更多DBA和devops內容,感興趣的朋友可以關注下!

詳解樂觀鎖、悲觀鎖以及它們各自的應用

順便慶祝一波RNG


分享到:


相關文章: