innodb mvcc實現機制


多版本並發控制

  大部分的MySQL的存儲 引擎,比如InnoDB,Falcon,以及PBXT並不是簡簡單單的使用行鎖機制。它們都使用了行鎖結合一種提高並發的技術,被稱為MVCC(多版本並 發控制)。MVCC並不單單應用在MySQL中,其他的數據庫如Oracle,PostgreSQL,以及其他數據庫也使用這個技術。

  MVCC避免了許多需要加鎖的情形以及降低消耗。這取決於它實現的方式,它允許非阻塞讀取,在寫的操作的時候阻塞必要的記錄。

  MVCC保存了某一時刻數據的一個快照。意思就是無論事物運行了多久,它們都能看到一致的數據。也就是說在相同的時間下,不同的事物看相同表的數據是不同的。如果你從來沒有這方面的經驗,可能說這些有點令人困惑。但是在以后這個會很容易理解和熟悉的。

  每個存儲引擎實現MVCC方式都是不同的。有許多種包含了樂觀(optimistic)和悲觀(pessimistic)的並發控制。我們用簡單的InnoDb的行為來舉例說明MVCC工作方式。

說道事物,我們必須了解一點基礎知識:

一、基礎知識

事務:

事務是一組原子性sql查詢語句,被當作一個工作單元。若mysql對改事務單元內的所有sql語句都正常的執行完,則事務操作視為成功,所有的sql語句才對數據生效,若sql中任意不能執行或出錯則事務操作失敗,所有對數據的操作則無效(通過回滾恢復數據)。事務有四個屬性:

1、原子性:事務被認為不可分的一個工作單元,要么全部正常執行,要么全部不執行。

2、一致性:事務操作對數據庫總是從一種一致性的狀態轉換成另外一種一致性狀態。

3、隔離性:一個事務的操作結果在內部一致,可見,而對除自己以外的事務是不可見的。

4、永久性:事務在未提交前數據一般情況下可以回滾恢復數據,一旦提交(commit)數據的改變則變成永久(當然用update肯定還能修改)。

ps:MYSAM 引擎的數據庫不支持事務,所以事務最好不要對混合引擎(如INNODB 、MYISAM)操作,若能正常運行且是你想要的最好,否則事務中對非支持事務表的操作是不能回滾恢復的。

讀鎖:

也叫共享鎖、S鎖,若事務T對數據對象A加上S鎖,則事務T可以讀A但不能修改A,其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S 鎖。這保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改。

寫鎖:

又稱排他鎖、X鎖。若事務T對數據對象A加上X鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的鎖。這保證了其他事務在T釋放A上的鎖之前不能再讀取和修改A。

表鎖:操作對象是數據表。Mysql大多數鎖策略都支持(常見mysql innodb),是系統開銷最低但並發性最低的一個鎖策略。事務t對整個表加讀鎖,則其他事務可讀不可寫,若加寫鎖,則其他事務增刪改都不行。

行級鎖:操作對象是數據表中的一行。是MVCC技術用的比較多的,但在MYISAM用不了,行級鎖用mysql的儲存引擎實現而不是mysql服務器。但行級鎖對系統開銷較大,處理高並發較好。

MVCC:多版本並發控制(MVCC,Multiversion Currency Control)。一般情況下,事務性儲存引擎不是只使用表鎖,行加鎖的處理數據,而是結合了MVCC機制,以處理更多的並發問題。Mvcc處理高並發能力最強,但系統開銷比最大(較表鎖、行級鎖),這是最求高並發付出的代價。

Autocommit:是mysql一個系統變量,默認情況下autocommit=1表示mysql把沒一條sql語句自動的提交,而不用commit語句。所以,當要開啟事務操作時,要把autocommit設為0,可以通過“set session autocommit=0; ”來設置

二、MVCC實現原理以及例化理解(包含些測試以便理解)

第一:先看看網絡上幾乎全部一樣的理解,包括《高性能mysql第二版(中文版)》也如此說明,這樣是很容易理解。但筆者覺得2個地方不妥,先看內容,在后面筆者會給出不妥地方用(12…)加粗標志出來,且給出測試證明。

Ps:這些只是外部看來的理解層面,深層次在第三點講解

------------------------------------------

InnoDB實現MVCC的方法是,它存儲了每一行的兩個(1)額外的隱藏字段,這兩個隱藏字段分別記錄了行的創建的時間和刪除的時間。在每個事件發生的時候,每行存儲版本號,而不是存儲事件實際發生的時間。每次事物的開始這個版本號都會增加。自記錄時間開始,每個事物都會保存記錄的系統版本號。依照事物的 版本來檢查每行的版本號。在事物隔離級別為可重復讀的情況下,來看看怎樣應用它。

SELECT

Innodb檢查沒行數據,確保他們符合兩個標准:

     1、InnoDB只查找版本早於當前事務版本的數據行(也就是數據行的版本必須小於等於事務的版本),這確保當前事務讀取的行都是事務之前已經存在的,或者是由當前事務創建或修改的行

     2、行的刪除操作的版本一定是未定義的或者大於當前事務的版本號。確定了當前事務開始之前,行沒有被刪除(2)

  符合了以上兩點則返回查詢結果。

  INSERT

     InnoDB為每個新增行記錄當前系統版本號作為創建ID。

  DELETE

     InnoDB為每個刪除行的記錄當前系統版本號作為行的刪除ID。

  UPDATE

InnoDB復制了一行。這個新行的版本號使用了系統版本號。它也把系統版本號作為了刪除行的版本。

----------------------------------------------

(1)    不是兩個,是三個。

1DB_TRX_ID:一個6byte的標識,每處理一個事務,其值自動+1,上述說到的“創建時間”和“刪除時間”記錄的就是這個DB_TRX_ID的值,如insert、update、delete操作時,刪除操作用1個bit表示。 DB_TRX_ID是最重要的一個,可以通過語句“show engine innodb status”來查找,如下:

  -----------------------------------------

  ……

     TRANSACTIONS

------------

Trx id counter 0 430621

Purge done for trx\'s n:o < 0 430136 undo n:o < 0 0

History list length 7

……

  ------------------------------------------

2DB_ROLL_PTR: 大小是7byte,指向寫到rollback segment(回滾段)的一條undo log記錄(update操作的話,記錄update前的ROW值)

3DB_ROW_ID: 大小是6byte,該值隨新行插入單調增加,當由innodb自動產生聚集索引時,聚集索引包括這個DB_ROW_ID的值,不然的話聚集索引中不包括這個值. 這個用於索引當中

(2)    這里的不是真正的刪除數據,而是標志出來的刪除。真正意義的刪除是在commit的時候。網上的說法很容易讓讀者誤解

(3)    這點上面沒有標注,在insert操作時 “創建時間”=DB_ROW_ID,這時,“刪除時間 ”是未定義的;在update時,復制新增行的“創建時間”=DB_ROW_ID,刪除時間未定義,舊數據行“創建時間”不變,刪除時間=該事務的DB_ROW_ID;delete操作,相應數據行的“創建時間”不變,刪除時間=該事務的DB_ROW_ID;select操作對兩者都不修改,只讀相應的數據

第二、下面用圖形化形式表示MVCC如何處理select、insert、delete、update

有兩個事務A、B

假設開始時間順序ABCD,且DB_TRX_ID滿足以下情況

A. DB_TRX_ID = 2010

B. DB_TRX_ID = 2011

C. DB_TRX_ID = 2012

D. DB_TRX_ID = 2013

注意:

1、B. DB_TRX_ID> A. DB_TRX_ID是因為DB_TRX_ID的值是系統版本號的值,系統版本號是自動增加的,所以DB_TRX_ID也是自動增加。但是會出現這種情況,假如A事務開始后B事務開始前有一個insert操作插入一行數據(沒有bengin、comint),則B. DB_TRX_ID= A. DB_TRX_ID+1+1 ,並不符合不是說系統版本號增量為1。

   InnoDB實現MVCC的方法是,它存儲了每一行的兩個額外的隱藏字段,這兩個隱藏字段分別記錄了行的創建的時間和刪除的時間。在每個事件發生的時 候,每行存儲版本號,而不是存儲事件實際發生的時間。每次事物的開始這個版本號都會增加。自記錄時間開始,每個事物都會保存記錄的系統版本號。依照事物的 版本來檢查每行的版本號。在事物隔離級別為可重復讀的情況下,來看看怎樣應用它。

  SELECT

  InnoDB檢查每行,要確定它符合兩個標准。

  InnoDB必須知道行的版本號,這個行的版本號至少要和事物版本號一樣的老。(也就是是說它的版本號可能少於或者和事物版本號相同)。這個既能確定事物開始之前行是存在的,也能確定事物創建或修改了這行。

  行的刪除操作的版本一定是未定義的或者大於事物的版本號。確定了事物開始之前,行沒有被刪除。

  符合了以上兩點。會返回查詢結果。

  INSERT

  InnoDB記錄了當前新增行的系統版本號。

  DELETE

  InnoDB記錄的刪除行的系統版本號作為行的刪除ID。

  UPDATE

  InnoDB復制了一行。這個新行的版本號使用了系統版本號。它也把系統版本號作為了刪除行的版本。

  所有其他記錄的結果保存是,從未獲得鎖的查詢。這樣它們查詢的數據就會盡可能的快。要確定查詢行要遵循這些標准。缺點是存儲引擎要為每一行存儲更多的數據,檢查行的時候要做更多的處理以及其他內部的一些操作。

  MVCC只能在可重復讀和可提交讀的隔離級別下生效。不可提交讀不能使用它的原因是不能讀取符合事物版本的行版本。它們總是讀取最新的行版本。可序列化不能使用MVCC的原因是,它總是要鎖定行。

  下面的表說明了在MySQL中不同鎖的模式以及並發級別。

鎖的策略                    並發性                開銷                  引擎
最低 最低 MyISAM,Merge,Memory
NDB Cluster
行和MVCC 最高 最高 InnoDB,Falcon,PBXT,solidD

在並發讀寫數據庫時,讀操作可能會不一致的數據(臟讀)。為了避免這種情況,需要實現數據庫的並發訪問控制,最簡單的方式就是加鎖訪問。由於,加鎖會將讀寫操作串行化,所以不會出現不一致的狀態。但是,讀操作會被寫操作阻塞,大幅降低讀性能。在Java concurrent包中,有copyonwrite系列的類,專門用於優化讀遠大於寫的情況。而其優化的手段就是,在進行寫操作時,將數據copy一份,不會影響原有數據,然后進行修改,修改完成后原子替換掉舊的數據,而讀操作只會讀取原有數據。通過這種方式實現寫操作不會阻塞讀操作,從而優化讀效率。而寫操作之間是要互斥的,並且每次寫操作都會有一次copy,所以只適合讀大於寫的情況。

再啰嗦幾句,MVCC的原理與copyonwrite類似,每個讀操作會看到一個一致性的snapshot,並且可以實現非阻塞的讀。MVCC允許數據具有多個版本,這個版本可以是時間戳或者是全局遞增的事務ID,在同一個時間點,不同的事務看到的數據是不同的。

實現原理: 

------------------------------------------------------------------------------------------> 時間軸

|-------R(T1)-----|

|-----------U(T2)-----------|

如上圖,假設有兩個並發操作R(T1)和U(T2),T1和T2是事務ID,T1小於T2,系統中包含數據a = 1(T1),R和W的操作如下:

R:read a (T1)

U:a = 2    (T2)

R(讀操作)的版本T1表示要讀取數據的版本,而之后寫操作才會更新版本,讀操作不會。在時間軸上,R晚於U,而由於U在R開始之后提交,所以對於R是不可見的。所以,R只會讀取T1版本的數據,即a = 1。

由於在update操作提交之前,不能影響已有數據的一致性,所以不會改變舊的數據,update操作會被拆分成insert + delete。需要標記刪除舊的數據,insert新的數據。只有update提交之后,才會影響后續的讀操作。而對於讀操作而且,只能讀到在其之前的所有的寫操作,正在執行中的寫操作對其是不可見的。

上面說了一堆的虛的理論,下面來點干活,看一下MySQL的innodb引擎是如何實現MVCC的。innodb會為每一行添加兩個字段,分別表示該行創建的版本和刪除的版本,填入的是事務的版本號,這個版本號隨着事務的創建不斷遞增。在repeated read的隔離級別(事務的隔離級別請看這篇文章)下,具體各種數據庫操作的實現:

select:滿足以下兩個條件innodb會返回該行數據:(1)該行的創建版本號小於等於當前版本號,用於保證在select操作之前所有的操作已經執行落地。(2)該行的刪除版本號大於當前版本或者為空。刪除版本號大於當前版本意味着有一個並發事務將該行刪除了。

insert:將新插入的行的創建版本號設置為當前系統的版本號。

delete:將要刪除的行的刪除版本號設置為當前系統的版本號。

update:不執行原地update,而是轉換成insert + delete。將舊行的刪除版本號設置為當前版本號,並將新行insert同時設置創建版本號為當前版本號。

其中,寫操作(insert、delete和update)執行時,需要將系統版本號遞增。

由於舊數據並不真正的刪除,所以必須對這些數據進行清理,innodb會開啟一個后台線程執行清理工作,具體的規則是將刪除版本號小於當前系統版本的行刪除,這個過程叫做purge。

通過MVCC很好的實現了事務的隔離性,可以達到repeated read級別,要實現serializable還必須加鎖。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM