欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mysql InnoDB多版本并發(fā)控制MVCC詳解

 更新時間:2022年11月29日 08:33:15   作者:Cuzzz  
這篇文章主要介紹了Mysql InnoDB多版本并發(fā)控制MVCC詳解的相關(guān)資料,需要的朋友可以參考下

一丶為什么需要事務(wù)隔離級別

mysql是一個客戶端/服務(wù)端軟件,對于同一個服務(wù)器來說,可以有多個客戶端進(jìn)行連接,每一個客戶端進(jìn)行連接之后就形成一個會話,每一個客戶端都可以在自己的會話中向服務(wù)器發(fā)出請求語句,一個請求語句可能是某一個事務(wù)的一部分,服務(wù)器可以同時處理多個事務(wù)。

如果事務(wù)時一個接著一個進(jìn)行,那么下一個事務(wù)是在上一個事務(wù)的一致性前提下進(jìn)行的,就沒用一致性的問題,但是事務(wù)是并發(fā)進(jìn)行且可能訪問到相同的數(shù)據(jù)這時候就會出現(xiàn)如下問題

可以看到AB最開始總和13元,最后AB總和18元,銀行血虧五元,這顯然違背了一致性——錢的總量不變。這就是并發(fā)情況下兩個事務(wù)的影響,所以需要事務(wù)隔離讓事務(wù)隔離的進(jìn)行,互不干涉。

1.實現(xiàn)事務(wù)隔離的方式:串行執(zhí)行

最簡單直接的方式,同一時間只能有一個事務(wù)運行,這樣必然不會有上述不一致的情況,但是大大降低了吞吐率并增加了事務(wù)的等待時間

2.實現(xiàn)事務(wù)隔離的方式:可串行執(zhí)行

并發(fā)事務(wù)之所以出現(xiàn)不一致的情況,就是由于多個事務(wù)訪問相同的數(shù)據(jù),需要實現(xiàn)多個事務(wù)在訪問相同數(shù)據(jù)的時候進(jìn)行限制,比方說上圖中事務(wù)2想訪問A賬戶的值需要等待事務(wù)提交事務(wù)之后,這樣可以讓并發(fā)事務(wù)的執(zhí)行如同串行執(zhí)行的效果一樣。

二丶并發(fā)事務(wù)執(zhí)行的問題:臟寫,臟讀,不可重復(fù)讀,幻讀

1.臟寫

一個事務(wù)修改了另外一個未提交事務(wù)修改過的數(shù)據(jù)

臟寫導(dǎo)致一致性無法保證

上圖事務(wù)A和事務(wù)B都更新紫色數(shù)據(jù),其中事務(wù)A首先更新為A,然后事務(wù)B過來更新為B,這時候事務(wù)A回滾后更新為Null,事務(wù) B 明明正常寫了一行數(shù)據(jù),但是寫完之后發(fā)現(xiàn)值變了,有點丟失更新的意思。(比如A表示余額,這時候在將余額A判斷是否足以支付,判斷得到可以,事務(wù)B執(zhí)行扣費寫入A-5,商家收到5元,結(jié)果這時候回滾了,A變成Null,事務(wù)A中轉(zhuǎn)錢的一方錢變?yōu)锳,錢的總額變?yōu)锳+5了)

臟寫導(dǎo)致原子性受到破壞

假如上述的事務(wù)B還操作了另外的數(shù)據(jù),比如插入一條數(shù)據(jù)C,并且更改為B寫入C是在一個事務(wù)下面的,需要具備原子性,但是臟寫讓B的更改需要部分回滾為Null,這樣插入C和更改B就不具備原子性(比如A表示余額,這時候在將余額A判斷是否足以支付,判斷得到可以,事務(wù)B執(zhí)行扣費寫入A-5,商家收到5元,結(jié)果這時候回滾了,A變成Null,這時候部分回滾,商家的5元沒用回滾,商家的庫存也沒用回滾,原子性被破壞)

2.臟讀

如果一個事務(wù)讀取到另外一個事務(wù)未提交的數(shù)據(jù),意味著發(fā)生了臟讀

比如事務(wù)A先寫數(shù)據(jù)A,然后事務(wù)B督導(dǎo)數(shù)據(jù)A后在內(nèi)存中使用A進(jìn)行一系列操作(比如A表示余額,這時候在將余額A判斷是否足以支付,判斷得到可以)但是事務(wù)A這時候回滾了,事務(wù)B再次讀取數(shù)據(jù)發(fā)現(xiàn)為null,這就是臟讀。

臟讀可能引發(fā)一致性的問題:比如事務(wù)操作時修改x和y的值,并且二者總是相等的,A修改x為1,還沒來得及修改y也沒用提交事務(wù),這時候事務(wù)B讀取x=1,y=0,二者不等,事務(wù)B讀取到了數(shù)據(jù)庫不一致的狀態(tài),讀取到未提交事務(wù)的值

3.不可重復(fù)讀

假如一個事務(wù)修改了另外一個事務(wù)未提交的數(shù)據(jù),意味發(fā)生了不可重復(fù)讀

比如事務(wù)A第一次讀取到值為A,接著事務(wù)B修改為B,并且提交了事務(wù)B,然后事務(wù)A再次讀取得到的數(shù)據(jù)是B,同一行數(shù)據(jù)多次讀取值并不相同,這稱作不可重復(fù)讀。它是指在同一個事務(wù)里面查詢同一行數(shù)據(jù),每次查到的數(shù)據(jù)都不一樣。和臟讀區(qū)別在于臟讀是由于別的事務(wù)回滾導(dǎo)致,而不可重復(fù)讀讀到的其實是已經(jīng)提交的數(shù)據(jù)。

事務(wù)A讀到事務(wù)B提交后的數(shù)據(jù)似乎很合理,但是我們想象這樣一種場景:你有一個流水表和用戶余額,其中記錄用戶每天的流水,你在月初0點的時候核對流水和庫存,但是流水很多,你的程序選擇一個一個用戶的進(jìn)行核對,核對用戶甲,甲沒做任何消費,但是當(dāng)你核對B的時候,你將B的流水load到內(nèi)存中,但是B這時候(0點30分,這一筆數(shù)據(jù)新的一個余額)進(jìn)行了扣除余額的操作,導(dǎo)致B余額和流水對不上了。

4.幻讀

如果一個事務(wù)A先根據(jù)沒用搜索條件查詢到一些記錄,在該事務(wù)未提交前,另外一個事務(wù)寫入(delete,update,insert)了符合搜索條件的記錄,這時候事務(wù)A再次讀取,發(fā)現(xiàn)數(shù)據(jù)條數(shù)和第一次讀取的不同,如同出現(xiàn)了幻覺,稱之為幻讀

事務(wù)A讀到事務(wù)B提交后的數(shù)據(jù)似乎很合理,但是我們想象這樣一種場景:你有一個需求將會公司的男性員工了女性員工查詢進(jìn)行展示,你先查詢了總數(shù)為100人,然后查詢男性的總數(shù)50人,后查詢女性人數(shù)準(zhǔn)備在頁面展示共100人,其中男50人,女50人,結(jié)果這是管理信息的人發(fā)現(xiàn)有一位員工性別錯誤錄入了,將其從男修改為女,這時候你讀取事務(wù)就是女51人了,你在主頁顯示了共100人,其中男50人,女51人

三丶隔離級別

1.Read UnCommitted 讀未提交

在此隔離級別下,會發(fā)生臟讀,不可重復(fù)讀,和幻讀

2.Read Committed 讀已提交

在此隔離級別下,會發(fā)生不可重復(fù)讀,和幻讀

3.Repeatable Read 可重復(fù)讀

在此隔離級別下,可能發(fā)生幻讀

4.Serializable 可串行化

在此隔離級別下,不會發(fā)生臟讀,不可重復(fù)讀,和幻讀

其中臟寫是對一致性影響最嚴(yán)重的,無論是何種隔離級別,都不允許臟寫發(fā)生,innodb使用鎖保證不會出現(xiàn)臟寫現(xiàn)象,第一個事務(wù)更新某條記錄的時候,會給這條記錄加鎖,另外一個事務(wù)在此更新的時候,需要等待第一個事務(wù)提交釋放鎖后更新。隔離級別越高,其并發(fā)能力越低。

四丶Mysql設(shè)置隔離級別

默認(rèn)隔離級別可重復(fù)讀

1.設(shè)置全局隔離級別

SET GLOBAL TRANSACTION ISOLATION LEVEL 期望的隔離級別(可選READ UNCOMMITED,READ COMMITED,REPEATABLE READ,SERIALIZABLE),此命令只對執(zhí)行語句后新產(chǎn)生的會話有效,對當(dāng)前已經(jīng)存在的會話無效

2.設(shè)置會話隔離級別

SET SESSION TRANSACTION ISOLATION LEVEL 期望的隔離級別(可選READ UNCOMMITED,READ COMMITED,REPEATABLE READ,SERIALIZABLE),對當(dāng)前會話后續(xù)事務(wù)有效,該語句可以在已開啟的事務(wù)中執(zhí)行,但是不會影響當(dāng)前正在執(zhí)行的事務(wù),如果在事務(wù)之間執(zhí)行,只會對后續(xù)的事務(wù)有效

3.設(shè)置下一個事務(wù)的隔離級別

SET TRANSACTION ISOLATION LEVEL 期望的隔離級別(可選READ UNCOMMITED,READ COMMITED,REPEATABLE READ,SERIALIZABLE) 只對當(dāng)前會話的下一個即將開啟的事務(wù)有效,下一個事務(wù)執(zhí)行完后,后續(xù)事務(wù)將恢復(fù)到之前的隔離級別,該語句不能再已經(jīng)開啟的事務(wù)中執(zhí)行,否則會報錯。

4.指定服務(wù)器的隔離級別

在啟動的時候使用--transaction-isolation=xxx即可執(zhí)行默認(rèn)隔離級別

五丶MVCC原理

下面討論記錄對當(dāng)前事務(wù)是否可見都是基于當(dāng)前事務(wù)中執(zhí)行的查詢是快照讀(普通查詢),對于當(dāng)前讀(select xxx for update,select xxx lock in share mode)是不通用的

1.版本鏈

對于InnoDB存儲引擎來說,其聚簇索引記錄中包含兩個隱藏列:

trx_id:一個事務(wù)每次對聚簇索引記錄做出改動的時候,都會把該事務(wù)的事務(wù)id復(fù)制給此列 roll_point:每次對某條聚簇索引記錄進(jìn)行改動的時,都會把舊的版本寫入到undo 日志中,此列相當(dāng)于一個指針,指向修改前的信息

每次修改都會形成Undo 日志,所有版本的數(shù)據(jù)會通過roll_point串聯(lián)成一個鏈表,稱之為版本鏈,頭節(jié)點是當(dāng)前記錄的最新值。利用版本鏈控制多個并發(fā)事務(wù)訪問相同記錄時的行為稱為MVCC多版本并發(fā)控制。

其實在undo日志中,只記錄被更新列的信息,而不是記錄全部的信息,對于沒有記錄的列,會通過版本鏈找少一個版本中的對應(yīng)列的信息,直到找到聚簇索引葉子節(jié)點中的內(nèi)容

2.Read View

對于使用Read Uncommitted隔離級別的事務(wù),可以讀取到?jīng)]提交的數(shù)據(jù),那么直接讀取最新的版本即可。對于Serializable隔離級別,innodb直接通過加鎖來訪問記錄。對于read committed 和 repeatable read隔離級別的事務(wù),都必須保證督導(dǎo)的數(shù)據(jù)是已經(jīng)提交事務(wù)修改過的記錄,那么如何判斷版本鏈中的哪個版本的數(shù)據(jù)是當(dāng)前事務(wù)可見的昵?

innodb 使用的Read View

2.1 read view 的結(jié)構(gòu)

  • m_ids:在生成read view時,當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)id列表
  • min_trx_id:生成read view時,當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)中最小事務(wù)id,也就是m_ids中的最小值
  • max_trx_id:生成read view時,系統(tǒng)應(yīng)該分配給下一個事務(wù)的事務(wù)id值
  • creator_trx_id:生成該read view的事務(wù)的事務(wù)id

2.2 read view

  • 如果被訪問版本的trx_idcreator_trx_id相同,意味著當(dāng)前事務(wù)在訪問自己修改的記錄,自然可見
  • 如果訪問版本的trx_id屬性值小于read view中的min_trx_id 表明此版本是生成read view之前已經(jīng)提交的事務(wù),那么自然可見
  • 如果訪問版本的trx_id,大于等于read view中的max_trx_id說明,當(dāng)前版本數(shù)據(jù)是生成read view后開啟事務(wù)產(chǎn)生的,那么自然不可見
  • 如果訪問版本的trx_id 介于min_trx_idmax_trx_id之間,需要判斷trx_id是否位于m_ids列表中,如果在說明創(chuàng)建read view時生成該版本的事務(wù)還是活躍的,那么該版本,不可被訪問,如果不在說明創(chuàng)建read view 時生成該版本的事務(wù)已經(jīng)提交,可以被訪問到

如果某個版本數(shù)據(jù)對當(dāng)前事務(wù)不可見那么需要一直順著版本鏈找上一個版本的數(shù)據(jù),并通過上述步驟判斷是否可見,直到找到可見的版本,如果一直找不到說明該條記錄對當(dāng)前事務(wù)不可見,查詢結(jié)果將不包含該記錄。

2.3 Read Committed和 Repeatable Read的不同

Read Committed——每次讀取數(shù)據(jù)前都生成一個Read View

這樣可以保證生成Read view 中的m_ids是實時活躍事務(wù)id集合,也許第一次讀取的時候事務(wù)A沒提交,其id位于m_ids中,但是第二次讀取的時候事務(wù)A提交了,事務(wù)A將不位于m_ids中,這樣在第二次讀取的時候,通過m_ids判斷事務(wù)A是否提交的時候,可以得到事務(wù)A已經(jīng)提交了,然后讓事務(wù)A版本產(chǎn)生的數(shù)據(jù)可見(見2.2.4中的內(nèi)容)。

Repeatable Read——如果使用begin開啟事務(wù)那么在第一次查詢的時候生成Read view,如果使用start transaction with consistent snapshot 那么執(zhí)行的時候就會生成read view

這樣可以保證當(dāng)前事務(wù)從頭到尾都是read view中記錄的內(nèi)容是一致的,第一次讀取的時候事務(wù)A沒有提交,那么不可見,但是第二次讀取的時候事務(wù)A提交了,但是read view的m_idsmax_trx_id可以判斷事務(wù)A不可見,比如事務(wù)A事務(wù)id小于max_trx_id意味著生成read view是事務(wù)A啟動但是沒提交,即使第二次讀事務(wù)A提交了,但是m_ids中還是包含事務(wù)A,那么不可見。如果事務(wù)A事務(wù)id大于max_trx_id,那么自然第二次還是大于max_trx_id,也是不可見的,從而實現(xiàn)了可重復(fù)讀。

2.4 二級索引與MVCC

上面我們提到,innodb聚簇索引組織的記錄才具備trx_idroll_point,那么我們使用二級索引進(jìn)行查詢的時候,如何判斷數(shù)據(jù)是否可見昵?

  • 二級索引頁面的page header中存在page_max_trx_id屬性,每當(dāng)有事務(wù)對其中的記錄進(jìn)行增刪改查操作的時候,如果事務(wù)的事務(wù)id,大于page_max_trx_id,那么會更新page_max_trx_id屬性值為其事務(wù)id,這意味著page_max_trx_id記錄了修改該二級索引頁面最大的事務(wù)id是多少。當(dāng)select通過二級索引首先看下對于read view的min_trx_id是否大于該頁面的page_max_trx_id,如果大于那么頁面中所有記錄都對該read view可見,否則就進(jìn)行下面的第二步
  • 利用二級索引中的主鍵值,進(jìn)行回標(biāo),得到對應(yīng)的聚簇索引記錄然后進(jìn)行回表,然后通過2.2中步驟拿到第一個可見版本的數(shù)據(jù),然后比對此紀(jì)錄和通過二級索引查詢得到記錄的值是否相同,如果相同那么發(fā)送給客戶端,否則跳過該記錄。

到此這篇關(guān)于Mysql InnoDB多版本并發(fā)控制MVCC詳解的文章就介紹到這了,更多相關(guān)Mysql InnoDB多版本并發(fā)控制MVCC內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論