資料庫中的悲觀鎖和樂觀鎖
為了得到最大的效能,一般資料庫都有併發機制,不過帶來的問題就是資料訪問的衝突。為了解決這個問題,大多數資料庫用的方法就是資料的鎖定。
1、悲觀鎖,正如其名,它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制(也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改資料)。
2、樂觀鎖( Optimistic Locking )
相對悲觀鎖而言,樂觀鎖機制採取了更加寬鬆的加鎖機制。悲觀鎖大多數情況下依靠資料庫的鎖機制實現,以保證操作最大程度的獨佔性。但隨之而來的就是資料庫效能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。
而樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基於資料版本( Version )記錄機制實現。何謂資料版本?即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是透過為資料庫表增加一個 “version” 欄位來實現。讀取出資料時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交資料的版本資料與資料庫表對應記錄的當前版本資訊進行比對,如果提交的資料版本號大於資料庫表當前版本號,則予以更新,否則認為是過期資料。
考慮下面的情況:如果我們先查詢到資料,然後更新資料,這樣會出現這樣的情況。
A執行緒查詢的時候,B執行緒也在查詢,當A執行緒準備更新的時候,B執行緒先獲得 了更新鎖,將這些行鎖定了。A只能等待B更新完。當B執行緒更新完釋放鎖的時候,A獲得鎖,這時A會識別出欄位已經修改,所以會重新查詢資料,然後開始更新。
這在資料庫中本來是沒什麼問題的,但應用程式中會出現一個問題。
假如有一個修改商品資訊的頁面
A,開啟這個頁面,得到商品資料。有編號 ,名稱 ,價格。
B,也開啟這個頁面,得到商品資料。有編號 ,名稱 ,價格。
然後A修改商品的價格,然後點儲存。
B修改商品的名稱,點儲存。
這裡有一個問題是,當A儲存修改後的商品價格時,他實際上儲存了原編號,原名稱,修改後價格。
然後B再點儲存的時候,他實際上儲存了原編號,修改後的名稱,原價格。
這樣,B的修改就將A的修改覆蓋了,導致A的修改無效。
那麼,怎麼解決這種問題呢?
這裡有兩種方法解決這個問題,也就是所謂的樂觀鎖和悲觀鎖。
悲觀鎖
悲觀鎖是這樣實現的。
當查詢到商品資訊,並試圖更新資料時,在查詢的語句後加上for update nowait
這表示當A從準備更新這個資料(查詢這個資料並顯示到頁面上)開始,這個資料就被鎖定了,當你更新完成,才釋放鎖。
當B也準備更新這個資料時,因為有for update nowait,查詢這個資料時並顯示到頁面上就會出錯。所以不會更新。
這樣就能保證每次只有一個準備更新的連線。從而就保證了資料不會出現丟失更新
悲觀鎖假定其他使用者企圖訪問或者改變你正在訪問、更改的物件的機率是很高的,因此在悲觀鎖的環境中,在你開始改變此物件之前就將該物件鎖住,並且直 到你提交了所作的更改之後才釋放鎖。悲觀的缺陷是不論是頁鎖還是行鎖,加鎖的時間可能會很長,這樣可能會長時間的限制其他使用者的訪問,也就是說悲觀鎖的並 發訪問性不好。
也就是說,當我們找到需要更改的物件時,就把這些行鎖定,只能讓我修改。當我修改完成時,再釋放這個鎖,別人才能修改。這樣就能防止我找到需要更改的物件到我開始更新資料這段時間內,其他執行緒獲得鎖先更新資料的情況。
樂觀鎖
樂觀鎖是這樣實現的。
每次更新時都和舊版本的資料比較。具體如下:
A,開啟這個頁面,得到商品資料。有編號 ,名稱 ,價格。
B,也開啟這個頁面,得到商品資料。有編號 ,名稱 ,價格。
然後A修改商品的價格,然後點儲存。這時更新語句是這樣的
set 編號 = 原編號 ,名稱 = 原名稱 ,價格 =新價格 where 編號 = 原編號 ,名稱 = 原名稱 ,價格 =原價格
因為A更新時滿足WHERE後條件,所以更新成功。
當B修改商品的名稱,點儲存。語句是這樣的
set 編號 = 原編號 ,名稱 = 新名稱 ,價格 =原價格 where 編號 = 原編號 ,名稱 = 原名稱 ,價格 =原價格
此時價格已經是新的價格,所以價格(新價格) = 原價格 條件不滿足,所以更新不成功。
這樣就避免了丟失更新。
每個資料都和舊資料比較,可能比較麻煩,可以專門建一列,用作版本列。
當更新一次時,版本列資料加1。這樣然後A修改商品的價格,然後點儲存。這時更新語句是這樣的
set 編號 = 原編號 ,名稱 = 原名稱 ,價格 =新價格 ,版本 = 版本 + 1 where 編號 = 原編號 ,版本 = 版本
因為A更新時滿足WHERE後條件,所以更新成功。
當B修改商品的名稱,點儲存。語句是這樣的
set 編號 = 原編號 ,名稱 = 新名稱 ,價格 =原價格 ,版本 = 版本 + 1 where 編號 = 原編號 ,版本 = 版本
此時價格已經是新的版本 ,所以版本 (新版本 ) = 原版本 條件不滿足,所以更新不成功。
這裡的版本號也可以採用時間戳型別,這樣可以順便看到該行最後更新的時間。
樂觀鎖則認為其他使用者企圖改變你正在更改的物件的機率是很小的,因此樂觀鎖直到你準備提交所作的更改時才將物件鎖住,當你讀取以及改變該物件時並不加鎖。 可見樂觀鎖加鎖的時間要比悲觀鎖短,樂觀鎖可以用較大的鎖粒度獲得較好的併發訪問效能。但是如果第二個使用者恰好在第一個使用者提交更改之前讀取了該物件,那 麼當他完成了自己的更改進行提交時,資料庫就會發現該物件已經變化了,這樣,第二個使用者不得不重新讀取該物件並作出更改。這說明在樂觀鎖環境中,會增加並 發使用者讀取物件的次數。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/9399028/viewspace-1814949/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 悲觀鎖和樂觀鎖
- laravel樂觀鎖和悲觀鎖Laravel
- 理解樂觀鎖和悲觀鎖
- 一文讀懂資料庫中的樂觀鎖和悲觀鎖和MVVC資料庫
- 面試必備的資料庫悲觀鎖與樂觀鎖面試資料庫
- 解鎖你的資料庫:JPA和Hibernate的樂觀鎖與悲觀鎖資料庫
- Java中的鎖之樂觀鎖與悲觀鎖Java
- Java 中的悲觀鎖和樂觀鎖的實現Java
- 【每日鮮蘑】從資料庫看樂觀鎖、悲觀鎖資料庫
- MySQL樂觀鎖和悲觀鎖介紹MySql
- Redis的事務、樂觀鎖和悲觀鎖Redis
- mysql悲觀鎖以樂觀鎖MySql
- 利用MySQL中的樂觀鎖和悲觀鎖實現分散式鎖MySql分散式
- MySQL鎖(樂觀鎖、悲觀鎖、多粒度鎖)MySql
- SQLServer樂觀鎖定和悲觀鎖定例項SQLServer
- JPA和Hibernate的樂觀鎖與悲觀鎖
- java-樂觀鎖與悲觀鎖Java
- MybatisPlus - [03] 樂觀鎖&悲觀鎖MyBatis
- 樂觀鎖和悲觀鎖在kubernetes中的應用
- MySQL 悲觀鎖與樂觀鎖的詳解MySql
- 小議“悲觀鎖和樂觀鎖”的原理、場景、示例
- 樂觀鎖和悲觀鎖策略的區別與實現
- 面試必備之悲觀鎖與樂觀鎖面試
- 面試必備之樂觀鎖與悲觀鎖面試
- Java彌散系列 - 樂觀鎖與悲觀鎖Java
- SQL SERVER樂觀鎖定和悲觀鎖定使用例項SQLServer
- 【鎖機制】共享鎖、排它鎖、悲觀鎖、樂觀鎖、死鎖等等
- 樂觀鎖與悲觀鎖及應用舉例
- Java開發技巧——併發控制中的樂觀鎖與悲觀鎖Java
- MySQL/InnoDB中,樂觀鎖、悲觀鎖、共享鎖、排它鎖、行鎖、表鎖、死鎖概念的理解MySql
- 關於庫存超賣問題,悲觀鎖和樂觀鎖的不同實現
- 悲觀鎖與樂觀鎖的實現(詳情圖解)圖解
- 關於樂觀鎖與悲觀鎖的實際應用
- SSM (十五) 樂觀鎖與悲觀鎖的實際應用SSM
- 面試必備知識點:悲觀鎖和樂觀鎖的那些事兒面試
- Java併發程式設計(05):悲觀鎖和樂觀鎖機制Java程式設計
- Spring Boot2+JPA之悲觀鎖和樂觀鎖實戰Spring Boot
- 經典問題之樂觀鎖和悲觀鎖及使用場景