InnoDB MVCC與樂觀鎖

2021-08-26 08:33:53 字數 2951 閱讀 1425

最近通過《高效能mysql》一書學習mysql方面的知識,在看到書中所講innodb-mvcc部分的時候,有一種強烈的感覺,這不就是樂觀鎖嗎(入門級小學徒的疑惑臉)?當下便去網上以各種方式查詢閱讀mvcc和樂觀鎖相關的部落格,發現大部分的部落格對於這兩者之間的關係都隻字不提,提了的也是眾說紛紜,關於兩者關係的細節方面也十分曖昧沒有定論。在暫時無法得出最終結論的情況下,我先談談在學習這方面知識後我自己對兩者的理解,然後試著得出自己的結論,正確與否大家一起思考。

在解釋mvcc之前,我首先引用《高效能mysql》書中原文來解釋一下隔離級別:

那隔離級別到底和mvcc有什麼關係呢?如果說將資料庫比作一輛汽車,然後將隔離級別比作汽車輪轂,那麼mvcc就是abs防抱死制動系統,不過這個abs防抱死制動系統只適用於read committed和repeatable read兩個型號的輪轂。

上面解釋隔離級別時提到了,在repeatable read隔離級別下,儘管解決了不可重複讀,但還是存在幻讀的問題。如果要避免幻讀,就得在事務執行的時候加鎖,但是大量的鎖會嚴重影響效能。怎樣才能不通過加鎖還能解決幻讀呢?這就是mvcc要做的事情。

mvcc是multi-version concurrency control(多版本併發控制)的縮寫,很多資料庫都實現了mvcc,但是在不同的儲存引擎中mvcc的實現是不同的,今天所說的是innodb中的mvcc實現。innodb的mvcc,是通過在每行記錄後儲存兩個隱藏的列來實現的(使用者不可見)。乙個列儲存行建立的時間,乙個列儲存行過期(刪除)的時間,這裡所說的時間並不是傳統意義上的時間,而是系統版本號,下面是repeatable read隔離級別下mvcc的具體操作:

-select

innodb會根據以下兩個條件檢查每行記錄:

(1)innodb只查詢版本早於當前事務版本的資料行(行的系統版本號小於或者等於事務的系統版本號),這樣可以確保事務讀取到的行,要麼是在事務開始之前已經存在的,要麼是事務自身插入或者修改過的(結合以下insert、update操作理解)。

(2)行的刪除版本要麼未定義,要麼大於當前事務版本號。可以確保事務讀取到的行,在事務開啟之前未被刪除(結合以下delete操作理解)。

-insert

innodb為新插入的每一行儲存當前系統版本號作為行版本號。

-delete

innodb為刪除的每一行儲存當前系統版本號作為行刪除標識(第二個隱藏列的作用來了)。

-update

innodb將更新後的列作為新的行插入資料庫(並不是覆蓋),並儲存當前系統版本號作為該行的行版本號,同時儲存當前系統版本號到原來的行作為行刪除標識。

到這裡,mvcc是什麼以及它做了什麼事基本上已經說清楚了,為什麼在學習了mvcc後我會產生「這就是樂觀鎖」的想法呢(實際上很多人都有這種想法,在一些部落格裡也有人說mvcc就是樂觀鎖)?有這幾個原因。首先,innodb中mvcc和樂觀鎖(其實這麼說是不嚴謹的,後面會解釋為什麼)都是通過「不加鎖」的手段來實現加鎖的效果。其次它們的不加鎖手段都是通過版本號去控制的。通過這兩點也不難看出為什麼會有很多人在mvcc和樂觀鎖之間產生疑問。

那麼樂觀鎖是怎麼實現的呢?最常見的就是通過資料版本(version)記錄機制實現。資料版本和innodb-mvcc中的系統版本作用相似不做過多解釋。通過為資料庫表增加乙個數字型別的字段作為版本標識version(使用者可見,欄位名自定),當讀取資料時,將其version的值一同讀出,資料每更新一次,version都增加1,當提交更新的時候,判斷資料庫表對應行的當前版本資訊與第一次讀取出來的version值進行對比,如果一致,則給與更新,否則不予更新(可以不涉及事務,但是mvcc機制必須依託於事務,事實上隔離級別本就是事務的隔離級別)。具體操作如下:

select id, name, version from testable;(例如id=1,version=1024)

update testable set name=』張三』,version=version+1

從上面的所有文字中,我們還是無法得出乙個有效的結論,只看得出innodb-mvcc和文中所提到的樂觀鎖確實很像,它們到底是何關係我們還是無從所知。那我們再來看看《高效能mysql》中所提到的一句話:不同儲存引擎的mvcc實現是不同的,典型的有樂觀併發控制和悲觀併發控制。看完這句話我們再結合上文,可以得出這樣乙個結論:mvcc並不是樂觀鎖,innodb所實現的mvcc才是樂觀鎖(當然也有其他儲存引擎利用樂觀併發控制的思想實現mvcc),更嚴謹一點來說,樂觀鎖並不是一種具體的技術,樂觀鎖只是一種併發控制的思想,所有認為「併發事務不算大」而採用非加鎖的形式來實現「加鎖」效果的控制機制我們都認為它是樂觀鎖。既然如此,那我們之前提到的樂觀鎖就不能叫樂觀鎖了,它只是樂觀鎖的一種表達方式,是一種在使用者行為上通過非加鎖的方式來實現併發控制的手段。同樣的mvcc更不能稱之為樂觀鎖,只能說innodb實現的mvcc是一種在系統行為上通過非加鎖的方式來實現併發控制的手段。

總結來說,innodb-mvcc是一種系統行為,在repeatable read隔離級別下,它通過樂觀併發控制解決了該隔離級別所不能解決的幻讀,但是前提是這些都得依託於事務的封裝。儘管如此,它還是無法完全解決一些併發業務場景下的問題,並且過多的事務使用會嚴重影響系統的效能,這就需要通過使用者行為去約束(最開始所提到的樂觀鎖)。

所以,最後的結論就是,mvcc並非樂觀鎖,但是innodb儲存引擎所實現的mvcc是樂觀的,它和之前所提到的使用者行為的「樂觀鎖」都採用的是樂觀機制,屬於不同的「樂觀鎖」手段,它們都是「樂觀家族」的成員。

悲觀鎖與樂觀鎖

悲觀鎖與樂觀鎖 悲觀鎖 pessimistic locking 顧名思義就是採用一種悲觀的態度來對待事務併發問題,我們認為系統中的併發更新會非常頻繁,並且事務失敗 了以後重來的開銷很大,這樣以來,我們就需要採用真正意義上的鎖來進行實現。悲觀鎖的基本思想就是每次一 個事務讀取某一條記錄後,就會把這條記...

樂觀鎖與悲觀鎖

悲觀鎖 pessimistic locking 顧名思義就是採用一種悲觀的態度來對待事務併發問題,我們認為系統中的併發更新會非常頻繁,並且事務失敗了以後重來的開銷很大,這樣以來,我們就需要採用真正意義上的鎖來進行實現。悲觀鎖的基本思想就是每次乙個事務讀取某一條記錄後,就會把這條記錄鎖住,這樣 其它的...

樂觀鎖與悲觀鎖

鎖 locking 這個概念在我們學習多執行緒的時候曾經接觸過,其實這裡的鎖和多執行緒裡面處理併發的鎖是乙個道理,都是暴力的把資源歸為自己所有。這裡我們用到鎖的目的就是通過一些機制來保證一些資料在某個操作過程中不會被外界修改,這樣的機制,在這裡,也就是所謂的 鎖 即給我們選定的目標資料上鎖,使其無法...