【mysql】關於innodb中MVCC的一些理解

1、MVCC簡介

MVCC (Multiversion Concurrency Control),即多版本併發控制技術,它使得大部分支持行鎖的事務引擎,再也不單純的使用行鎖來進行數據庫的併發控制,取而代之的是把數據庫的行鎖與行的多個版本結合起來,只須要很小的開銷,就能夠實現非鎖定讀,從而大大提升數據庫系統的併發性能php

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

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

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

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

2、MVCC實現原理

innodb MVCC主要是爲Repeatable-Read事務隔離級別作的。在此隔離級別下,A、B客戶端所示的數據相互隔離,互相更新不可見併發

瞭解innodb的行結構、Read-View的結構對於理解innodb mvcc的實現由重要意義mvc

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檢查每行數據,確保他們符合兩個標準:

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

二、行的刪除操做的版本必定是未定義的或者大於當前事務的版本號,肯定了當前事務開始以前,行沒有被刪除

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

INSERT

InnoDB爲每一個新增行記錄當前系統版本號做爲建立ID。

DELETE

InnoDB爲每一個刪除行的記錄當前系統版本號做爲行的刪除ID。

UPDATE

InnoDB複製了一行。這個新行的版本號使用了系統版本號。它也把系統版本號做爲了刪除行的版本。

說明

insert操做時 「建立時間」=DB_ROW_ID,這時,「刪除時間 」是未定義的;

update時,複製新增行的「建立時間」=DB_ROW_ID,刪除時間未定義,舊數據行「建立時間」不變,刪除時間=該事務的DB_ROW_ID;

delete操做,相應數據行的「建立時間」不變,刪除時間=該事務的DB_ROW_ID;

select操做對二者都不修改,只讀相應的數據

3、對於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=289http://blogread.cn/it/article/5969http://blog.csdn.net/chen77716/article/details/6742128http://blog.chinaunix.net/link.php?url=http://forge.mysql.com%2Fwiki%2FMySQL_Internals

相關文章
相關標籤/搜索