如今咱們簡單聊一下數據庫中的悲觀鎖和樂觀鎖。數據庫
悲觀鎖正如其名稱,比較悲觀。總會認爲:每當修改數據時,會有其餘線程也會同時修改該數據。因此針對這種狀況悲觀鎖的作法是:讀取數據以後就加鎖
(eg: select...for update)
,這樣別的線程讀取該數據的時候就須要等待當前線程釋放鎖,得到到鎖的線程才能得到該數據的讀寫權限。從而保證了併發修改數據錯誤的問題。可是因爲阻塞緣由,因此致使吞吐量不高。
悲觀鎖更適用於多寫少讀的狀況。
場景: 同窗A和同窗B都要給你轉500塊錢(開心壞了吧,這樣最終你能獲得1000塊錢)。
使用悲觀鎖的流程:
併發
balance = 0
並對該條記錄加鎖。balance=balance + 500 = 500
balance = 500
,並對該條記錄加鎖(若是你人緣好,此時同窗C給你轉帳也是須要等待同窗B轉帳完成才能夠轉帳哦)balance = balance + 500 = 1000
假設轉帳過程沒有鎖,咱們看看會發生什麼:
高併發
balance_a = 0
(沒有加鎖,此時同窗B也能夠獲取到帳戶餘額)balance_b = 0
balance = balance_a + 500 = 500
balance = balance_b + 500 = 500
丟失的500塊去哪裏了呢?從第2步能夠看到同窗B獲取到的帳戶餘額是0,而不是同窗A轉賬以後的餘額500。因此問題出在這裏,這是高併發場景的常見問題。因此加鎖是很是必須的。可是加了悲觀鎖,同窗都要排隊給我轉帳,對於沒有耐心的同窗就直接不轉賬了,我豈不是錯失了發財的好機會。那有什麼好辦法呢?答案就是下面的樂觀鎖spa
樂觀鎖顧名思義比較樂觀,他只有在 更新數據的時候纔會檢查這條數據是否被其餘線程更新了(這點與悲觀鎖同樣,悲觀鎖是在讀取數據的時候就加鎖了)。若是更新數據時,發現這條數據被其餘線程更新了,則這次更新失敗。若是數據未被其餘線程更新,則更新成功。因爲 樂觀鎖沒有了鎖等待,提升了吞吐量,因此樂觀鎖適合多讀少寫的場景。常見的樂觀鎖實現方式是:版本號version和CAS(compare and swap)。此處只介紹版本號方式。線程
要採用版本號,首先須要在數據庫表中新增一個字段version,表示此條記錄的更新版本,記錄每變更一次,版本號加1。依舊使用上面轉帳的例子說明:
3d
balance = 0
和版本號version_a = 0
balance = 0
和版本號version_b = 0
update table set balance = ${balance}, version = version + 1 and version = 0
。(此時版本號爲0,因此更新成功)update table set balance = ${balance}, version = version + 1 and version = 0
。(此時版本號爲1,因此更新失敗,更新失敗以後同窗B再轉一次便可)悲觀鎖:讀取時加鎖,更新完釋放鎖,再此過程當中會形成其餘線程阻塞,致使吞吐量低,適用於多寫場景。code
樂觀鎖:不加鎖,只有在更新時驗證數據是否被其餘線程更新,吞吐量較高,適用於多讀場景。blog