1. 程式人生 > >悲觀鎖與樂觀鎖簡單介紹

悲觀鎖與樂觀鎖簡單介紹

1、悲觀鎖,正如其名,它指的是對資料被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個資料處理過程中,將資料處於鎖定狀態。悲觀鎖的實現,往往依靠資料庫提供的鎖機制(也只有資料庫層提供的鎖機制才能真正保證資料訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系
統不會修改資料)。
2、樂觀鎖( Optimistic Locking )
相對悲觀鎖而言,樂觀鎖機制採取了更加寬鬆的加鎖機制。悲觀鎖大多數情況下依靠資料庫的鎖機制實現,以保證操作最大程度的獨佔性。但隨之而來的就是資料庫效能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。
而樂觀鎖機制在一定程度上解決了這個問題。樂觀鎖,大多是基於資料版本( Version )記錄機制實現。何謂資料版本?即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表增加一個 “version” 欄位來實現。讀取出資料時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交資料的版本資料與資料庫表對應記錄的當前版本資訊進行比對,如果提交的資料版本號大於資料庫表當前版本號,則予以更新,否則認為是過期資料。

本質上,資料庫的樂觀鎖做法和悲觀鎖做法主要就是解決下面假設的場景,避免丟失更新問題:
一個比較清楚的場景
下面這個假設的實際場景可以比較清楚的幫助我們理解這個問題:
假設噹噹網上使用者下單買了本書,這時資料庫中有條訂單號為001的訂單,其中有個status欄位是’有效’,表示該訂單是有效的;
後臺管理人員查詢到這條001的訂單,並且看到狀態是有效的
使用者發現下單的時候下錯了,於是撤銷訂單,假設執行這樣一條SQL: update order_table set status = ‘取消’ where order_id = 001;
後臺管理人員由於在b這步看到狀態有效的,這時,雖然使用者在c這步已經撤銷了訂單,可是管理人員並未重新整理介面,看到的訂單狀態還是有效的,於是點選”發貨”按鈕,將該訂單發到物流部門,同時執行類似如下SQL,將訂單狀態改成已發貨:update order_table set status = ‘已發貨’ where order_id = 001
其實之前已經分別對樂觀鎖的做法和悲觀鎖的做法做了詳細的分析,
這裡引用wiki的定義做更權威的引用說明

http://en.wikipedia.org/wiki/Lock_%28database%29
There are mechanisms employed to manage the actions of multiple concurrent users on a database – the purpose is to prevent lost updates and dirty reads. The two types of locking are Pessimistic and Optimistic Locking.
Pessimistic locking: A user who reads a record, with the intention of updating it, places an exclusive lock on the record to prevent other users from manipulating it. This means no one else can manipulate that record until the user releases the lock. The downside is that users can be locked out for a very long time, thereby slowing the overall system response and causing frustration.
Where to use pessimistic locking: This is mainly used in environments where data-contention (the degree of users request to the database system at any one time) is heavy; where the cost of protecting data through locks is less than the cost of rolling back transactions if concurrency conflicts occur. Pessimistic concurrency is best implemented when lock times will be short, as in programmatic processing of records. Pessimistic concurrency requires a persistent connection to the database and is not a scalable option when users are interacting with data, because records might be locked for relatively large periods of time. It is not appropriate for use in web application development.
本質上,這裡wiki的意思就是,悲觀鎖和樂觀鎖都是為了解決丟失更新問題或者是髒讀。悲觀鎖和樂觀鎖的重點就是是否在讀取記錄的時候直接上鎖。悲觀鎖的缺點很明顯,需要一個持續的資料庫連線,這在web應用中已經不適合了。

以下是幾種錯誤的觀點:
觀點1:只有衝突非常嚴重的系統才需要悲觀鎖;
分析:這是更準確的說法;我在原文中說到:
“所有悲觀鎖的做法都適合於狀態被修改的概率比較高的情況,具體是否合適則需要根據實際情況判斷。”,表達的也是這個意思,不過說法不夠準確;的確,之所以用悲觀鎖就是因為兩個使用者更新同一條資料的概率高,也就是衝突比較嚴重的情況下,所以才用悲觀鎖。
觀點2:最後提交前作一次select for update檢查,然後再提交update也是一種樂觀鎖的做法
分析:這是更準確的說法;
的確,這符合傳統樂觀鎖的做法,就是到最後再去檢查。但是wiki在解釋悲觀鎖的做法的時候,’It is not appropriate for use in web application development.’, 現在已經很少有悲觀鎖的做法了,所以我自己將這種二次檢查的做法也歸為悲觀鎖的變種,因為這在所有樂觀鎖裡面,做法和悲觀鎖是最接近的,都是先select for update,然後update
除了上面的觀點1和觀點2是更準確的說法,下面的所有觀點都是錯誤的**
觀點3:這個問題的原因是因為資料庫隔離級別是uncommitted read級別;
分析:這個觀點是錯誤的;
這個過程本身就是在read committed隔離級別下發生的,從a到d每一步,尤其是d這步,並不是因為讀到了未提交的資料,僅僅是因為使用者介面沒有重新整理[事實上也不可能做自動重新整理,這樣相當於資料庫一發生改變立刻要重新整理了,這需要監聽資料庫了,顯然這是簡單問題複雜化了];
觀點4:悲觀鎖是指一個使用者在更新資料的時候,其他使用者不能讀取這條記錄;也就是update阻塞讀才叫悲觀鎖;
分析:這個觀點是錯的;
這在db2背景的開發中尤其常見;因為db2預設就是update會阻塞讀;但是這是各個資料庫對讀寫的時候上鎖的併發處理實現不一樣。但這根本不是悲觀鎖樂觀鎖的區別。Oracle可以做到寫不阻塞讀僅僅是因為做了多版本併發控制(Multiversion concurrency control), http://en.wikipedia.org/wiki/Multiversion_concurrency_control;
但是在Oracle裡面,一樣可以做樂觀鎖和悲觀鎖的控制。這本質上是應用層面的選擇。
觀點5:Oracle實際上用的就是樂觀鎖
分析:這個觀點是錯的;
前面說了,Oracle的確可以做到寫不阻塞讀,但是這不是悲觀鎖和樂觀鎖的問題。這是因為實現了多版本併發控制。按照wiki的定義,悲觀鎖和樂觀鎖是在應用層面選擇的。Oracle的應用只要在第二步做了select for update,就是悲觀鎖的做法;
況且Oracle在任何隔離級別下,除了分散式事務兩階段提交的短暫時間,其他所有情況下都不存在寫阻塞讀的情況,如果按照這個觀點的話那Oracle已經不能做悲觀鎖了-_-
觀點6:不需要這麼麻煩,只需要在d這步,最後提交更新的時候再做一個普通的select檢查一下就可以;[就是double check的做法]
分析:這個觀點是錯的。
這個做法其實在http://www.hetaoblog.com/database-lost-update-pessimistic-lock/,’3. 傳統悲觀鎖做法的變通’這節已經說明了,如果要這麼做的話,仍然需要在最後提交更新前double check的時候做一個select for update, 否則select結束到update提交前的時間仍然有可能記錄被修改;
觀點7:應該儘可能使用悲觀鎖;
分析:這個觀點是錯的;
a. 根據悲觀鎖的概念,使用者在讀的時候(b這步)就會將記錄鎖住,直到更新結束的時候才會將鎖釋放,所以整個鎖的過程時間比較長;
b. 另外,悲觀鎖需要有一個持續的資料庫連線,這在當今的web應用中已經幾乎不存在;wiki上也說了, 悲觀鎖‘is not appropriate for use in web application development.’
所以,現在大部分應用都應該是樂觀鎖的;