樂觀鎖和 MVCC 的區別?

我在看這兩篇文章,多版本並發控制(MVCC)在分散式系統中的應用,InnoDB的多版本並發控制(MMVC)。感覺樂觀鎖和MVCC好像是一樣的東西啊,誰能告訴我他們有什麼區別,還是說就是同樣的東西?


MVCC 可以保證不阻塞地讀到一致的數據。但是,MVCC 並沒有對實現細節做約束,為此不同的資料庫的語義有所不同,比如:

  • postgres 是嚴格地無鎖,對寫操作也是樂觀並發控制;在表中保存同一行數據記錄的多個不同版本,每次寫操作,都是創建,而迴避更新;在事務提交時,按版本號檢查當前事務提交的數據是否存在寫衝突,則拋異常告知用戶,回滾事務;
  • innodb 則只對讀無鎖,寫操作仍是上鎖的悲觀並發控制,這也意味著,innodb 中只能見到因死鎖和不變性約束而回滾,而見不到因為寫衝突而回滾;不像 postgres 那樣對數據修改在表中創建新紀錄,而是每行數據只在表中保留一份,在更新數據時上行鎖,同時將舊版數據寫入 undo log;表和 undo log 中行數據都記錄著事務ID,在檢索時,只讀取來自當前已提交的事務的行數據;

可見 MVCC 中的寫操作仍可以按悲觀並發控制實現,而 CAS 的寫操作只能是樂觀並發控制。

還有一個不同在於,MVCC 在語境中傾向於 「對多行數據打快照造平行宇宙」,然而 CAS 一般只是保護單行數據而已。比如 mongodb 有 CAS 的支持,但不能說這是 MVCC。


MVCC是多版本並發控制,同一個數據有多個版本,事務開啟時看到是哪個版本就看到這個版本,最大的好處是讀寫不衝突,只有寫於寫是衝突的,這個特性可以很大程度上提升性能

而樂觀鎖就是另外一個東西,它是一個前提假設,遇到讀寫衝突需要回退,在資料庫的實現中,一般使用悲觀鎖,即:操作數據之前先拿到對應的鎖


在資料庫中,並發控制是指在多個用戶/進程/線程同時對資料庫進行操作時,如何保證事務的一致性和隔離性的,同時最大程度地並發。

當多個用戶/進程/線程同時對資料庫進行操作時,會出現3種衝突情形:

  1. 讀-讀,不存在任何問題
  2. 讀-寫,有隔離性問題,可能遇到臟讀(會讀到未提交的數據) ,幻影讀等。
  3. 寫-寫,可能丟失更新

要解決衝突,一種辦法是是鎖,即基於鎖的並發控制,比如2PL,這種方式開銷比較高,而且無法避免死鎖。

多版本並發控制(MVCC)是一種用來解決讀-寫衝突的無鎖並發控制,也就是為事務分配單向增長的時間戳,為每個修改保存一個版本,版本與事務時間戳關聯,讀操作只讀該事務開始前的資料庫的快照。 這樣在讀操作不用阻塞寫操作,寫操作不用阻塞讀操作的同時,避免了臟讀和不可重複讀

樂觀並發控制(OCC)是一種用來解決寫-寫衝突的無鎖並發控制,認為事務間爭用沒有那麼多,所以先進行修改,在提交事務前,檢查一下事務開始後,有沒有新提交改變,如果沒有就提交,如果有就放棄並重試。樂觀並發控制類似自選鎖。樂觀並發控制適用於低數據爭用,寫衝突比較少的環境。

多版本並發控制可以結合基於鎖的並發控制來解決寫-寫衝突,即MVCC+2PL,也可以結合樂觀並發控制來解決寫-寫衝突。


MVCC解決的問題是讀寫互相不阻塞的問題,每次更新都產生一個新的版本,讀的話可以讀歷史版本。試想,如果一個數據只有一個版本,那麼多個事務對這個數據進行讀寫是不是需要讀寫鎖來保護? 一個讀寫事務在運行的過程中在訪問數據之前先加讀/寫鎖這種實現叫做悲觀鎖,悲觀體現在,先加鎖,獨佔數據,防止別人加鎖。樂觀鎖呢,讀寫事務,在真正的提交之前,不加讀/寫鎖,而是先看一下數據的版本/時間戳,等到真正提交的時候再看一下版本/時間戳,如果兩次相同,說明別人期間沒有對數據進行過修改,那麼就可以放心提交。樂觀體現在,訪問數據時不提前加鎖。在資源衝突不激烈的場合,用樂觀鎖性能較好。如果資源衝突嚴重,樂觀鎖的實現會導致事務提交的時候經常看到別人在他之前已經修改了數據,然後要進行回滾或者重試,還不如一上來就加鎖。



沒有區別,屬於包含與被包含的關係,樂觀鎖包含 MVCC,MVCC 只是樂觀鎖的一種實現方式。


樂觀鎖:基於鎖機制進行並發控制 (配合CAS實現)

MVCC理論:基於快照隔離機制(Snapshot Isolations)進行並發控制

讀: 無鎖無阻塞

寫: 配合悲觀鎖 / CAS方式

關於MVCC標準的具體實現,不同DB廠商不同的MVCC實現機制,fleuria已經說明了


持續關注,這裡好像好沒說太明白。

在MYSQL下,MVCC和樂觀鎖的區別


MySQL的大多數事務型存儲引擎實現的都不是簡單的行級鎖。基於提升並發性能的考慮,它們一般都同時實現了多版本並發控制(MVCC)。不僅是MySQL,包括Oracle、PostgreSQL等其他資料庫系統也都實現了MVCC,但各自的實現機制不盡相同,因為MVCC沒有一個統一的實現標準。

可以認為MVCC是行級鎖的一個變種,但是它在很多情況下避免了加鎖操作,因此開銷更低。雖然實現機制有所不同,但大都實現了非阻塞的讀操作,寫操作也只是鎖定必要的行。

MVCC的實現,是通過保存數據在某個時間點的快照來實現的。也就是說,不管需要執行多長時間,每個事務看到的數據都是一致的。根據事務開始的時間不同,每個事務對同一張表,同一時刻看到的數據可能是不一樣的。

前面說到不同的存儲引擎的MVCC實現是不同的,典型的有樂觀並發控制和悲觀並發控制。

——《高性能MySQL 第三版》第一章第四小節

怎麼有人會理解成樂觀鎖包含MVCC?


在 SQL ANSI標準中,read/write, write/read是相互block,是為了保持數據的一致性。因此所有的資料庫廠商都使用鎖來保證數據的一致性

但是具體而言, oracle沒有實現read阻塞write, 而是使用MVCC的技術解決了write/read相互

阻塞的問題,導致的結果是oracle中block發生的頻率遠少於sqlserver

sql server 2000 及以前的版本中,完全實現了read 和write的相互block, 所以sql server中會產生

大量的blocking問題。

在sql 2005及以後的版本,微軟聽從開放人員的建議,利用MVCC技術也實現了read和write互不block,但是這不是默認開啟的,需要對資料庫做一些設置,而我的觀察是很多的公司任然沒有開啟

MVCC。

所以通常我們把沒有開啟MVCC特性的,使用原來的鎖機制來保證數據一致性的這種鎖叫悲觀鎖,

而對開啟MVCC機制的鎖,叫做樂觀鎖。


推薦閱讀:

TAG:資料庫 | SQL | 並發控制 | 並發 | 事務並發 |