From:http://www.linuxidc.com/Linux/2011-09/42266.htm
Mysql到底是怎麼實作MVCC的?這個問題無數人都在問,但google中并無答案,本文嘗試從Mysql源碼中尋找答案。
在Mysql中MVCC是在Innodb存儲引擎中得到支援的,Innodb為每行記錄都實作了三個隐藏字段:
- 6位元組的事務ID(
)DB_TRX_ID
- 7位元組的復原指針(DB_ROLL_PTR)
- 隐藏的ID
6位元組的事物ID用來辨別該行所述的事務,7位元組的復原指針需要了解下Innodb的事務模型。
1. Innodb的事務相關概念
為了支援事務,Innbodb引入了下面幾個概念:
-
redo log
redo log就是儲存執行的SQL語句到一個指定的Log檔案,當Mysql執行recovery時重新執行redo log記錄的SQL操作即可。當用戶端執行每條SQL(更新語句)時,redo log會被首先寫入log buffer;當用戶端執行COMMIT指令時,log buffer中的内容會被視情況重新整理到磁盤。redo log在磁盤上作為一個獨立的檔案存在,即Innodb的log檔案。
-
undo log
與redo log相反,undo log是為復原而用,具體内容就是copy事務前的資料庫内容(行)到undo buffer,在适合的時間把undo buffer中的内容重新整理到磁盤。undo buffer與redo buffer一樣,也是環形緩沖,但當緩沖滿的時候,undo buffer中的内容會也會被重新整理到磁盤;與redo log不同的是,磁盤上不存在單獨的undo log檔案,所有的undo log均存放在主ibd資料檔案中(表空間),即使用戶端設定了每表一個資料檔案也是如此。
-
rollback segment
復原段這個概念來自Oracle的事物模型,在Innodb中,undo log被劃分為多個段,具體某行的undo log就儲存在某個段中,稱為復原段。可以認為undo log和復原段是同一意思。
-
鎖
Innodb提供了基于行的鎖,如果行的數量非常大,則在高并發下鎖的數量也可能會比較大,據Innodb文檔說,Innodb對鎖進行了空間有效優化,即使并發量高也不會導緻記憶體耗盡。
對行的鎖有分兩種:排他鎖、共享鎖。共享鎖針對對,排他鎖針對寫,完全等同讀寫鎖的概念。如果某個事務在更新某行(排他鎖),則其他事物無論是讀還是寫本行都必須等待;如果某個事物讀某行(共享鎖),則其他讀的事物無需等待,而寫事物則需等待。通過共享鎖,保證了多讀之間的無等待性,但是鎖的應用又依賴Mysql的事務隔離級别。
-
隔離級别
隔離級别用來限制事務直接的互動程度,目前有幾個工業标準:
- READ_UNCOMMITTED:髒讀
- READ_COMMITTED:讀送出
- REPEATABLE_READ:重複讀
- SERIALIZABLE:串行化
Innodb對四種類型都支援,髒讀和串行化應用場景不多,讀送出、重複讀用的比較廣泛,後面會介紹其實作方式。
2. 行的更新過程
下面示範下事務對某行記錄的更新過程:
1. 初始資料行
F1~F6是某行列的名字,1~6是其對應的資料。後面三個隐含字段分别對應該行的事務号和復原指針,假如這條資料是剛INSERT的,可以認為ID為1,其他兩個字段為空。
2.事務1更改該行的各字段的值
當事務1更改該行的值時,會進行如下操作:
- 用排他鎖鎖定該行
- 記錄redo log
- 把該行修改前的值Copy到undo log,即上圖中下面的行
- 修改目前行的值,填寫事務編号,使復原指針指向undo log中的修改前的行
3.事務2修改該行的值
與事務1相同,此時undo log,中有有兩行記錄,并且通過復原指針連在一起。 是以,如果undo log一直不删除,則會通過目前記錄的復原指針回溯到該行建立時的初始内容,所幸的時在Innodb中存在purge線程,它會查詢那些比現在最老的活動事務還早的undo log,并删除它們,進而保證undo log檔案不至于無限增長。
4. 事務送出
當事務正常送出時Innbod隻需要更改事務狀态為COMMIT即可,不需做其他額外的工作,而Rollback則稍微複雜點,需要根據目前復原指針從undo log中找出事務修改前的版本,并恢複。如果事務影響的行非常多,復原則可能會變的效率不高,根據經驗值沒事務行數在1000~10000之間,Innodb效率還是非常高的。很顯然,Innodb是一個COMMIT效率比Rollback高的存儲引擎。據說,Postgress的實作恰好與此相反。
5. Insert Undo log
上述過程确切地說是描述了UPDATE的事務過程,其實undo log分insert和update undo log,因為insert時,原始的資料并不存在,是以復原時把insert undo log丢棄即可,而update undo log則必須遵守上述過程。