mysql杯觀鎖與樂觀鎖

悲觀鎖與樂觀鎖是兩種常見的資源併發鎖設計思路,也是併發編程中一個很是基礎的概念。本文將對這兩種常見的鎖機制在數據庫數據上的實現進行比較系統的介紹。mysql

悲觀鎖(Pessimistic Lock)


悲觀鎖的特色是先獲取鎖,再進行業務操做,即「悲觀」的認爲獲取鎖是很是有可能失敗的,所以要先確保獲取鎖成功再進行業務操做。一般所說的「一鎖二查三更新」即指的是使用悲觀鎖。一般來說在數據庫上的悲觀鎖須要數據庫自己提供支持,即經過經常使用的select … for update操做來實現悲觀鎖。當數據庫執行select for update時會獲取被select中的數據行的行鎖,所以其餘併發執行的select for update若是試圖選中同一行則會發生排斥(須要等待行鎖被釋放),所以達到鎖的效果。select for update獲取的行鎖會在當前事務結束時自動釋放,所以必須在事務中使用。sql

這裏須要注意的一點是不一樣的數據庫對select for update的實現和支持都是有所區別的,例如oracle支持select for update no wait,表示若是拿不到鎖馬上報錯,而不是等待,mysql就沒有no wait這個選項。另外mysql還有個問題是select for update語句執行中全部掃描過的行都會被鎖上,這一點很容易形成問題。所以若是在mysql中用悲觀鎖務必要肯定走了索引,而不是全表掃描。數據庫

樂觀鎖(Optimistic Lock)


樂觀鎖的特色先進行業務操做,不到萬不得已不去拿鎖。即「樂觀」的認爲拿鎖多半是會成功的,所以在進行完業務操做須要實際更新數據的最後一步再去拿一下鎖就好。編程

樂觀鎖在數據庫上的實現徹底是邏輯的,不須要數據庫提供特殊的支持。通常的作法是在須要鎖的數據上增長一個版本號,或者時間戳,而後按照以下方式實現:併發

複製代碼
1. SELECT data AS old_data, version AS old_version FROM …;
2. 根據獲取的數據進行業務操做,獲得new_data和new_version
3. UPDATE SET data = new_data, version = new_version WHERE version = old_version
if (updated row > 0) {
    // 樂觀鎖獲取成功,操做完成
} else {
    // 樂觀鎖獲取失敗,回滾並重試
}
複製代碼

樂觀鎖是否在事務中其實都是無所謂的,其底層機制是這樣:在數據庫內部update同一行的時候是不容許併發的,即數據庫每次執行一條update語句時會獲取被update行的寫鎖,直到這一行被成功更新後才釋放。所以在業務操做進行前獲取須要鎖的數據的當前版本號,而後實際更新數據時再次對比版本號確認與以前獲取的相同,並更新版本號,便可確認這之間沒有發生併發的修改。若是更新失敗便可認爲老版本的數據已經被併發修改掉而不存在了,此時認爲獲取鎖失敗,須要回滾整個業務操做並可根據須要重試整個過程。oracle

總結

  • 樂觀鎖在不發生取鎖失敗的狀況下開銷比悲觀鎖小,可是一旦發生失敗回滾開銷則比較大,所以適合用在取鎖失敗機率比較小的場景,能夠提高系統併發性能性能

  • 樂觀鎖還適用於一些比較特殊的場景,例如在業務操做過程當中沒法和數據庫保持鏈接等悲觀鎖沒法適用的地方spa

相關文章
相關標籤/搜索