小議“悲觀鎖和樂觀鎖”的原理、場景、示例
[1] 博由
前幾天與一些朋友談到這個問題,之前有一些概念的上的涉及,但是並沒有相對深入的瞭解,因此找一些資料來幫助自己理解悲觀鎖和樂觀鎖的概念理解、場景、然後通過示例來闡述樂觀鎖和悲觀鎖的實現方式。
[2] 摘要
本文將從三個方面來闡述悲觀鎖和樂觀鎖,以理論到實踐的思維方式呈現出個人對悲觀鎖和樂觀鎖的理解。
[1] 悲觀鎖和樂觀鎖的理論知識
[2] 悲觀鎖和樂觀鎖的一般使用場景&優缺點
[3] 簡單實現樂觀鎖和悲觀鎖的
[3] 理論知識
[QA]什麼是悲觀鎖和樂觀鎖?
術語 | 描述 | 常見案例 |
---|---|---|
樂觀鎖 | 每次去拿資料的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個資料 | 版本號控制,適用於多讀少寫的場景 |
悲觀鎖 | 每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會block直到它拿到鎖 | DB的行鎖、表鎖等,適用於資料一致性比較高的場景 |
[?] 個人理解
假設每個操作的物件是一個或者多個資源,悲觀鎖:可以理解為很悲觀的看待資源許可權的訪問,因此每次去操作資源時,總是會try操作,問一問是否可以去訪問(這個問一問,就是去嘗試獲取鎖),只有獲取了鎖之後,才會開始放心操作資源了;然後樂觀鎖卻是相反的,很樂觀的看待資源,不關心這個資源是否有鎖,而是直接去訪問資源,至多檢查一下當前資源是不是最新的。
用虛擬碼解釋一下:
[1] 悲觀鎖:
while (!lock.tryGet) { // 一直等待獲取鎖許可權
// do something,會經歷等待鎖、獲取鎖、釋放鎖的過程,是比較佔用資源的,但是確保了資源的併發訪問可能出現的問題。
lock.release
break
}
[2] 樂觀鎖:
if (checkResourceVersion) { // 檢查資源版本是否一致
// do something
}else {
// 過期, 更新資料無效
}
[4] 案例
在大概瞭解了,悲觀鎖和樂觀鎖的概念之後,我們看看在實際生產中,具體有那些案例和場景使用到了悲觀鎖和樂觀鎖,以及其對應的一般問題解決方案。
[4.1] 樂觀鎖案例
一般實現樂觀鎖的方式:
[1] 版本號控制
[2] 時間戳控制
[4.1.1] 樂觀鎖 - 版本號控制案例
一般會在資料庫表增加一個version欄位,這個欄位標識當前資料的版本,每次更新操作都會version+=1;流程下圖:
[圖片引用]
http://www.javaweb1024.com/java/JavaWebzhongji/2015/09/06/847.html
[1] 過程描述
1,start transaction
2,first_version = get_cur_version() // 獲取當前資料版本
3,update_data(version+=1) // 更新操作版本號+1
4,cur_version = get_cur_version() // 提交更新時,獲取版本號
5,if first_version == cur_version // 比較提交時的版本號與第一次獲取的版本號,如果一致,那麼認為資源是最新的,可以更新
then commit
else rollback or raise exception // 否則回滾或者丟擲異常
[2] 原理描述
最關鍵的點,在於確保每次提交的資訊是最新的,認為是沒有競爭的或者說很少競爭的,通過version來標識每一次的資料更新操作,當存在併發時,同一個資料,會又多個使用者進行更新操作,如果通過樂觀鎖來實現,在多寫的情況下,會頻繁出現異常或者回滾,因此一般使用在多讀少寫的情況,以提高系統吞吐量。
[4.1.2] 樂觀鎖 - 時間戳控制案例
時間戳的方式與版本號實際上原理差不多,每次更新資料時,會更新該時間戳欄位,以標識資料的更新情況。
[過程描述]
1, start transaction
2, first_timestamp = get_cur_timestamp()
3, update_data(timestamp=get_sys_cur_timestamp)
4, if first_timestamp = get_cur_timestamp()
then commit
else rollback or raise Exception
[原理]
每次更新資料時,時間戳會記錄更新時間,如果出現併發更新,會導致A,B事務更新提交時讀取的不是事務起初讀取的時間戳,因而導致失敗,同樣適合於多讀少寫的場景。
[4.2] 悲觀鎖案例
悲觀鎖的實現一般都是通過鎖機制來實現的,鎖可以簡單理解為資源的訪問的入口。如果要對一個具有鎖屬性的資源執行訪問時,在更新操作時,需要持鎖權才能進行操作,但是往往這種操作可以保證資料的一致性和完整性。
在資料庫中,表鎖、行鎖都是通過悲觀鎖形式來實現的,通過模擬一下mysql的行鎖形式,來闡述悲觀鎖的執行機制:
事務A,事務B,當事務A對id=1的記錄加了for update行鎖之後,事務B如果想訪問id=1的記錄,會出現block,因為鎖已經被事務A佔有了,要麼事務A操作完成,然後執行事務B block的操作,要不等待超時。
[5] 場景
我們知道了樂觀鎖和悲觀鎖的概念,已經一般的使用方式,那麼我們還需要了解到的是:什麼時候使用悲觀鎖,什麼時候使用樂觀鎖?
[5.1] 什麼時候使用悲觀鎖?
一旦通過悲觀鎖鎖定一個資源,那麼其他需要操作該資源的使用方,只能等待直到鎖被釋放,好處在於可以減少併發,但是當併發量非常大的時候,由於鎖消耗資源,並且可能鎖定時間過長,容易導致系統效能下降,資源消耗嚴重。因此一般我們可以在併發量不是很大,並且出現併發情況導致的異常使用者和系統都很難以接受的情況下,會選擇悲觀鎖進行。
[5.2] 什麼時候使用樂觀鎖?
樂觀鎖實際上並沒用實際的鎖資源操作,就如上面概述的版本號和時間戳方式一樣,使用方都可以操作相應的資源,而當第一個使用方提交之後,其他使用方提交時,會出現異常(例如:程式碼版本控制器SVN,GIT),其可以增加系統的併發處理能力,但是如果併發導致了資源提交衝突,其他使用方需要重新讀取資源,會增加讀的次數,但是可以面對高併發場景,前提是如果出現提交失敗,使用者是可以接受的。因此一般樂觀鎖只用在高併發、多讀少寫的場景。
其中:GIT,SVN,CVS等程式碼版本控制管理器,就是一個樂觀鎖使用很好的場景,例如:A、B程式設計師,同時從SVN伺服器上下載了code.html檔案,當A完成提交後,此時B再提交,那麼會報版本衝突,此時需要B進行版本處理合並後,再提交到伺服器。這其實就是樂觀鎖的實現全過程。如果此時使用的是悲觀鎖,那麼意味者所有程式設計師都必須一個一個等待操作提交完,才能訪問檔案,這是難以接受的。
參考
[1]http://www.javaweb1024.com/java/JavaWebzhongji/2015/09/06/847.html
[2]http://blog.csdn.net/sd4015700/article/details/50162965
相關文章
- 悲觀鎖和樂觀鎖
- laravel樂觀鎖和悲觀鎖Laravel
- 理解樂觀鎖和悲觀鎖
- 經典問題之樂觀鎖和悲觀鎖及使用場景
- MySQL樂觀鎖和悲觀鎖介紹MySql
- Redis的事務、樂觀鎖和悲觀鎖Redis
- mysql悲觀鎖以樂觀鎖MySql
- MySQL鎖(樂觀鎖、悲觀鎖、多粒度鎖)MySql
- SQLServer樂觀鎖定和悲觀鎖定例項SQLServer
- 資料庫中的悲觀鎖和樂觀鎖資料庫
- JPA和Hibernate的樂觀鎖與悲觀鎖
- java-樂觀鎖與悲觀鎖Java
- MybatisPlus - [03] 樂觀鎖&悲觀鎖MyBatis
- Java 中的悲觀鎖和樂觀鎖的實現Java
- 面試官:你說說互斥鎖、自旋鎖、讀寫鎖、悲觀鎖、樂觀鎖的應用場景面試
- Java中的鎖之樂觀鎖與悲觀鎖Java
- MySQL 悲觀鎖與樂觀鎖的詳解MySql
- Java多執行緒的悲觀鎖與樂觀鎖及各自適用場景Java執行緒
- 樂觀鎖和悲觀鎖策略的區別與實現
- 利用MySQL中的樂觀鎖和悲觀鎖實現分散式鎖MySql分散式
- 面試必備之悲觀鎖與樂觀鎖面試
- 面試必備之樂觀鎖與悲觀鎖面試
- Java彌散系列 - 樂觀鎖與悲觀鎖Java
- 樂觀鎖和悲觀鎖在kubernetes中的應用
- SQL SERVER樂觀鎖定和悲觀鎖定使用例項SQLServer
- 【鎖機制】共享鎖、排它鎖、悲觀鎖、樂觀鎖、死鎖等等
- 樂觀鎖與悲觀鎖及應用舉例
- 解鎖你的資料庫:JPA和Hibernate的樂觀鎖與悲觀鎖資料庫
- 悲觀鎖與樂觀鎖的實現(詳情圖解)圖解
- 面試必備的資料庫悲觀鎖與樂觀鎖面試資料庫
- 關於樂觀鎖與悲觀鎖的實際應用
- SSM (十五) 樂觀鎖與悲觀鎖的實際應用SSM
- 面試必備知識點:悲觀鎖和樂觀鎖的那些事兒面試
- Java併發程式設計(05):悲觀鎖和樂觀鎖機制Java程式設計
- Spring Boot2+JPA之悲觀鎖和樂觀鎖實戰Spring Boot
- [轉帖]SQL Server 鎖機制 悲觀鎖 樂觀鎖 實測解析SQLServer
- 【每日鮮蘑】從資料庫看樂觀鎖、悲觀鎖資料庫
- Java鎖最全詳解:樂觀鎖/悲觀鎖+公平鎖/非公平鎖+獨享鎖/共享鎖Java