標籤:
一、MVCC簡介
MVCC (Multiversion Concurrency Control),即多版本並發控制技術,它使得大部分支援行鎖的事務引擎,不再單純的使用行鎖來進行資料庫的並發控制,取而代之的是把資料庫的行鎖與行的多個版本結合起來,只需要很小的開銷,就可以實現非鎖定讀,從而大大提高資料庫系統的並發效能
讀鎖:也叫共用鎖定、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實現原理
innodb MVCC主要是為Repeatable-Read交易隔離等級做的。在此隔離等級下,A、B用戶端所示的資料相互隔離,互相更新不可見
瞭解innodb的行結構、Read-View的結構對於理解innodb mvcc的實現由重要意義
innodb儲存的最基本row中包含一些額外的儲存資訊 DATA_TRX_ID,DATA_ROLL_PTR,DB_ROW_ID,DELETE BIT
6位元組的DATA_TRX_ID 標記了最新更新這條行記錄的transaction id,每處理一個事務,其值自動+1
7位元組的DATA_ROLL_PTR 指向目前記錄項的rollback segment的undo log記錄,找之前版本的資料就是通過這個指標
- 6位元組的DB_ROW_ID,當由innodb自動產生叢集索引時,叢集索引包括這個DB_ROW_ID的值,否則叢集索引中不包括這個值.,這個用於索引當中
DELETE BIT位用於標識該記錄是否被刪除,這裡的不是真正的刪除資料,而是標誌出來的刪除。真正意義的刪除是在commit的時候
具體的執行過程
begin->用獨佔鎖定鎖定該行->記錄redo log->記錄undo log->修改當前行的值,寫事務編號,復原指標指向undo log中的修改前的行
上述過程確切地說是描述了UPDATE的事務過程,其實undo log分insert和update undo log,因為insert時,原始的資料並不存在,所以復原時把insert undo log丟棄即可,而update undo log則必須遵守上述過程
下面分別以select、delete、 insert、 update語句來說明
SELECT
Innodb檢查每行資料,確保他們符合兩個標準:
1、InnoDB只尋找版本早於當前事務版本的資料行(也就是資料行的版本必須小於等於事務的版本),這確保當前事務讀取的行都是事務之前已經存在的,或者是由當前事務建立或修改的行
2、行的刪除操作的版本一定是未定義的或者大於當前事務的版本號碼,確定了當前事務開始之前,行沒有被刪除
符合了以上兩點則返回查詢結果。
INSERT
InnoDB為每個新增行記錄當前系統版本號碼作為建立ID。
DELETE
InnoDB為每個刪除行的記錄當前系統版本號碼作為行的刪除ID。
UPDATE
InnoDB複製了一行。這個新行的版本號碼使用了系統版本號碼。它也把系統版本號碼作為了刪除行的版本。
說明
insert操作時 “建立時間”=DB_ROW_ID,這時,“刪除時間 ”是未定義的;
update時,複製新增行的“建立時間”=DB_ROW_ID,刪除時間未定義,舊資料行“建立時間”不變,刪除時間=該事務的DB_ROW_ID;
delete操作,相應資料行的“建立時間”不變,刪除時間=該事務的DB_ROW_ID;
select操作對兩者都不修改,唯讀相應的資料
三、對於MVCC的總結上述更新前建立undo log,根據各種策略讀取時非阻塞就是MVCC,undo log中的行就是MVCC中的多版本,這個可能與我們所理解的MVCC有較大的出入,一般我們認為MVCC有下面幾個特點:
- 每行資料都存在一個版本,每次資料更新時都更新該版本
- 修改時Copy出目前的版本隨意修改,各個事務之間無幹擾
- 儲存時比較版本號碼,如果成功(commit),則覆蓋原記錄;失敗則放棄copy(rollback)
就是每行都有版本號碼,儲存時根據版本號碼決定是否成功,聽起來含有樂觀鎖的味道,而Innodb的實現方式是:
- 事務以獨佔鎖定的形式修改未經處理資料
- 把修改前的資料存放於undo log,通過復原指標與主要資料關聯
- 修改成功(commit)啥都不做,失敗則恢複undo log中的資料(rollback)
二者最本質的區別是,當修改資料時是否要獨佔鎖定定,如果鎖定了還算不算是MVCC? Innodb的實現真算不上MVCC,因為並沒有實現核心的多版本共存,undo log中的內容只是序列化的結果,記錄了多個事務的過程,不屬於多版本共存。但理想的MVCC是難以實現的,當事務僅修改一行記錄使用理想的MVCC模式是沒有問題的,可以通過比較版本號碼進行復原;但當事務影響到多行資料時,理想的MVCC據無能為力了。 比如,如果Transaciton1執行理想的MVCC,修改Row1成功,而修改Row2失敗,此時需要復原Row1,但因為Row1沒有被鎖定,其資料可能又被Transaction2所修改,如果此時復原Row1的內容,則會破壞Transaction2的修改結果,導致Transaction2違反ACID。 理想MVCC難以實現的根本原因在於企圖通過樂觀鎖代替二段提交。修改兩行資料,但為了保證其一致性,與修改兩個分布式系統中的資料並無區別,而二提交是目前這種情境保證一致性的唯一手段。二段提交的本質是鎖定,樂觀鎖的本質是消除鎖定,二者矛盾,故理想的MVCC難以真正在實際中被應用,Innodb只是借了MVCC這個名字,提供了讀的非阻塞而已。
參考文章
https://www.percona.com/blog/2014/12/17/innodbs-multi-versioning-handling-can-be-achilles-heel/
http://www.xdata.me/?p=289
http://blogread.cn/it/article/5969
http://blog.csdn.net/chen77716/article/details/6742128
http://blog.chinaunix.net/link.php?url=http://forge.mysql.com%2Fwiki%2FMySQL_Internals
【mysql】關於innodb中MVCC的一些理解