高效能MySQL學習筆記 5 MVCC

2021-07-23 16:35:18 字數 2905 閱讀 3694

#多版本控制

multi-version concurrency control,多版本控制,每次操作,copy乙份所要改的資料作為副本,副本之間通過乙個版本號字段區分,並將副本的版本號+1,如果是更新操作,資料在副本上修改完後,要更新時候檢視原紀錄的版本號是否是副本版本號-1,是,更新,否(說明有其他修改事務在這期間修改了資料,使其版本號更新了),失敗,重新取資料重新更新;如果是讀操作,則是根據隔離級別讀取小於等於當前事務版本號資料庫資料(並不更改版本號)。

上面的邏輯只是粗略說明mvcc是乙個怎麼樣的東西,實際各個儲存引擎的實現並不一定就這樣,事實上,mvcc只是乙個標準說明,而沒有說明具體的實現。

另外,有關事務隔離級別詳見:事務隔離級別。

##理論

高效能mysql一書上說的mvcc實現方式:

innodb的mvcc:通過給每條記錄後面儲存兩個隱藏的字段來實現:乙個是行的建立時間,乙個是行的刪除時間。當然,實際上存的不是時間值,而是系統版本號,每開始乙個新的事務,系統版本號會自動遞增。事務開始的版本號則是系統版本號,用來和查詢到的每行記錄的版本號作比較。下面是隔離級別為repeatable read下mvcc具體操作:

select

innodb檢查沒行資料,確保他們符合兩個標準:

1、innodb只查詢版本早於當前事務版本的資料行(也就是資料行的版本必須小於等於事務的版本),這確保當前事務讀取的行都是事務之前已經存在的,或者是由當前事務建立或修改的行。

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

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

insert

innodb新插入的行以當前系統版本號為行版本號。

delete

innodb為刪除的每一行儲存當前系統版本號作為刪除標識。

update

innodb複製了一行。這個新行的版本號使用了系統版本號。它也把系統版本號作為了刪除行的版本。

上面最重要的是系統版本號乙個概念,系統版本號其實就是這個表從建立到此時所經歷過的事務次數,開始為0。注意系統版本號不是行版本號,表的每個行的版本號一般是不一樣的,因為不是每次操作都是針對整個表的。有了這個概念下面就好理解了:

乙個事務過程:

//注意,以下只針對隔離級別為read commited和repeatable read,其他隔離級別後面再說

0.事務開始,以下步驟資料操作並沒有寫到資料庫中

1.系統版本號+1

2.事務版本號=系統版本號

3.進行操作

(1)讀操作:遍歷資料庫表,查詢版本號<=事務版本號的行(這裡包括了複製的行,等於是

因為可能這個事務對資料作了修改或增加),還有,這個時候可能有其他事務對資料做了修改,但

修改會改變行版本號(增加),選取<=,所以是看不見的(隔離級別控制),而若這個時候有其

他事務有增加新資料,同樣版本號》當前系統版本號,所以不會出現幻讀。

(2)插入操作:增加乙個新行,行版本號=系統版本號

(3)刪除操作:將該行的刪除版本號(注意不是行版本號)=系統版本號,注意,這個時候並

沒有物理刪除,只是做了乙個標記(如果最後不刪除,或則哪怕刪除,會存在類似undo.log檔案

中,用來回滾)

(4)更新操作:複製所更新行記錄,將原行進行刪除操作,新行版本號=系統版本號

4.提交事務(commit),試圖更新資料:

(0)系統版本號+1

(1)對比資料行的副本與原資料,若副本的行版本號=原資料行版本號,更新成功,否則失敗,回滾操作。

(2)若有行資料的刪除版本=當前系統版本號,刪除資料行。

5.操作成功,更新資料。

再說下其他兩個隔離級別:read uncommited和serializable,這兩個級別下,mvcc是沒用的,因為read uncommited總是讀取最新的資料,而不是符合當前事務版本的資料行,而serializable呢,則會使對當前所有讀取的行加鎖,所以mvcc完全沒用。

##實現

以上,其實說的還是理論上的東西,實際上的實現並不是完全如此,就比如第(4)點更新操作,是如何判斷複製行是哪條行的複製?所以還需要乙個字段,事實上,mysql也的確如此做的,每一行其實有增加三個隱藏字段:

1. 6位元組的事務id(db_trx_id ),這個就是系統版本號(也是行版本號)

2. 7位元組的回滾指標(db_roll_ptr),這個就是刪除版本號,實現上是用來回滾,只有當undo.log快取不足時才刪除。

3. 隱藏的id,這就是每條行的id,用以區分原紀錄與副本

其中,7位元組回滾指標有關innodb事務模型,我這裡簡單說兩個比較重要的概念:

redo.log:重做日誌,就是每次mysql在執行寫入資料前先把要寫的資訊儲存在重寫日誌中,但出現斷電,奔潰,重啟等等導致資料不能正常寫入期望資料時,伺服器可以通過redo.log中的資訊重新寫入資料。

undo log:撤銷日誌,與redo log恰恰相反,當一些更改在執行一半時,發生意外,而無法完成,則可以根據撤消日誌恢復到更改之前的狀態。

具體詳情,這裡我自己並沒有深入研究,詳情可以參考:mysql中的mvcc(老碼農的專欄)

。##mvcc與樂觀鎖

搜尋查了一下二者的關係,發現很多說法,眾說紛紜,這裡我個人覺得這兩者的關係是:

二者均是概念上的思想,並不是實現,而二者也不是等於或包含被包含關係,因為,樂觀鎖可以通過mvcc這種思路實現,也可以通過其他方法實現,比如cas;但是mvcc本身只是想實現非阻塞的讀,可以認為mvcc是行級鎖的乙個變種,很多情況避免了加鎖操作,但不是沒有加鎖,寫操作是有加鎖的,只是加的是必要的行,事實上mvcc的實現有樂觀併發控制,也有悲觀併發控制的。

#總結總的來說,mvcc就是用空間換時間,用複製一行來操作,而不是對行加鎖,減少了加鎖操作,之後檢視原行是否在這期間有被更新過,若無操作成功,若有操作失敗,回滾重新操作。適用於單行操作,如果過多多行更新操作的話,失敗率很高,效能效率很低。

高效能mysql學習筆記

此文已由作者朱笑天授權網易雲社群發布。筆者在工作之餘閱讀了一下高效能mysql,以下的內容對mysql的介紹以及書中涉及一些概念的總結歸納。1.mysql架構 1.最上層負責鏈結處理 認證授權 安全等 2.中間一層涵蓋了mysql的大多數核心功能。包括查詢解析 分析 優化 快取 內建函式 所有的誇儲...

《高效能mysql》學習

整型型別 tinyint,smallint,mediumint,int,bigint 有可選的unsigned屬性 表示不允許負值 例 tinyint unsigned 儲存的範圍是0 255,tinyint則表示 128 127 實數型別 float和double型別支援使用標準的浮點運算進行近似...

高效能MySQL學習筆記一 MySQL架構

mysql最與眾不同的特性是 它的儲存引擎架構,這種架構的設計將查詢處理以及其他系統任務和資料的儲存 提取相分離。這種處理和儲存相分離的設計可以在使用時根據效能 特性,以及其他需求來選擇資料儲存的方式。mysql預設採用自動提交模式。設定是否自動提交 1為自動提交,0禁用,禁用後,使用commit或...