一洛退、事務的啟動時機:begin/start transaction 命令并不是一個事務的起點毯欣,在執(zhí)行到它們之后的第一個操作InnoDB表的語句(第一個快照讀語句),事務才真正啟動。如果你想要馬上啟動一個事務棕硫,可以使用start transaction with consistent snapshot 這個命令。
二毛甲、在MySQL里年叮,“視圖”的兩個概念:
? ? ? ? 1)一個是view。是一個用查詢語句定義的虛擬表玻募,在調(diào)用的時候執(zhí)行查詢語句并生成結果只损。創(chuàng)建視圖的語法是create view ... ,而它的查詢方法與表一樣七咧。
? ? ? ? 2)一個是InnoDB在實現(xiàn)MVCC時用到的一致性讀視圖跃惫,即consistent read view,用于支持RC(Read Committed艾栋,讀提交)爆存、RR(Repeatable Read,可重復讀)隔離級別的實現(xiàn)蝗砾。
三终蒂、“快照”在MVCC里的工作方式:
????????在可重復讀隔離級別下,事務在啟動的時候就“拍了個快照”遥诉。注意拇泣,這個快照是基于整庫的。InnoDB里面每個事務有一個唯一的事務ID矮锈,叫作transaction id霉翔。它是在事務開始的時候向InnoDB的事務系統(tǒng)申請的,是按申請順序嚴格遞增的苞笨。而每行數(shù)據(jù)也都是有多個版本的债朵。每次事務更新數(shù)據(jù)的時候,都會生成一個新的數(shù)據(jù)版本瀑凝,并且把transaction id賦值給這個數(shù)據(jù)版本的事務ID序芦,記為row trx_id戚哎。同時半醉,舊的數(shù)據(jù)版本要保留,并且在新的數(shù)據(jù)版本中林艘,能夠有信息可以直接拿到它寥枝。也就是說宪塔,數(shù)據(jù)表中的一行記錄,其實可能有多個版本(row)囊拜,每個版本有自己的row trx_id某筐。而每種狀態(tài)并不是物理上都是真實存在的,只記錄了當前的狀態(tài)冠跷。如果需要之前的狀態(tài)南誊,需要根據(jù)當前版本和undo log計算出來的身诺。而undo log是儲存的是每個版本的數(shù)據(jù)。
四抄囚、InnoDB是怎么定義“100G”的快照的霉赡?
? ??????按照可重復讀的定義,一個事務啟動的時候怠苔,能夠看到所有已經(jīng)提交的事務結果同廉。但是之后仪糖,這個事務執(zhí)行期間柑司,其他事務的更新對它不可見。因此锅劝,一個事務只需要在啟動的時候聲明說攒驰,“以我啟動的時刻為準,如果一個數(shù)據(jù)版本是在我啟動之前生成的故爵,就認玻粪;如果是我啟動以后才生成的,我就不認诬垂,我必須要找到它的上一個版本”劲室。當然,如果“上一個版本”也不可見结窘,那就得繼續(xù)往前找很洋。還有,如果是這個事務自己更新的數(shù)據(jù)隧枫,它自己還是要認的喉磁。在實現(xiàn)上, InnoDB為每個事務構造了一個數(shù)組官脓,用來保存這個事務啟動瞬間协怒,當前正在“活躍”的所有事務ID”氨浚“活躍”指的就是孕暇,啟動了但還沒提交。數(shù)組里面事務ID的最小值記為低水位赤兴,當前系統(tǒng)里面已經(jīng)創(chuàng)建過的事務ID的最大值加1記為高水位芭商。這個視圖數(shù)組和高水位,就組成了當前事務的一致性視圖(read-view)搀缠。而數(shù)據(jù)版本的可見性規(guī)則铛楣,就是基于數(shù)據(jù)的row trx_id和這個一致性視圖的對比結果得到的。這個視圖數(shù)組把所有的row trx_id 分成了幾種不同的情況艺普。
這樣簸州,對于當前事務的啟動瞬間來說鉴竭,一個數(shù)據(jù)版本的row trx_id,有以下幾種可能:
1)如果落在綠色部分岸浑,表示這個版本是已提交的事務或者是當前事務自己生成的搏存,這個數(shù)據(jù)是可見的;
2)如果落在紅色部分矢洲,表示這個版本是由將來啟動的事務生成的璧眠,是肯定不可見的;
3)如果落在黃色部分读虏,那就包括兩種情況:
????a. 若 row trx_id在數(shù)組中责静,表示這個版本是由還沒提交的事務生成的,不可見盖桥;
????b. 若 row trx_id不在數(shù)組中灾螃,表示這個版本是已經(jīng)提交了的事務生成的,可見揩徊。(比低水位大腰鬼,但是在當前事務啟動前,就已經(jīng)提交了)
根據(jù)這個聲明塑荒,系統(tǒng)里面隨后發(fā)生的更新熄赡,就跟這個事務看到的內(nèi)容無關了。因為之后的更新齿税,生成的版本一定屬于上面的2或者3(a)的情況彼硫,而對它來說,這些新的數(shù)據(jù)版本是不存在的偎窘,所以這個事務的快照乌助,就是“靜態(tài)”的了。InnoDB利用了“所有數(shù)據(jù)都有多個版本”的這個特性陌知,實現(xiàn)了“秒級創(chuàng)建快照”的能力他托。
五、分析事務A的語句返回的結果:
mysql> CREATE TABLE `t` (
? `id` int(11) NOT NULL,
? `k` int(11) DEFAULT NULL,
? PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);
方法一:
假設:
事務A開始前仆葡,系統(tǒng)里面只有一個活躍事務ID是99赏参;
事務A、B沿盅、C的版本號分別是100把篓、101、102腰涧,且當前系統(tǒng)里只有這四個事務韧掩;
三個事務開始前,(1,1)這一行數(shù)據(jù)的row trx_id是90窖铡。
這樣疗锐,事務A的視圖數(shù)組就是[99,100], 事務B的視圖數(shù)組是[99,100,101], 事務C的視圖數(shù)組是[99,100,101,102]坊谁。
第一個有效更新是事務C,把數(shù)據(jù)從(1,1)改成了(1,2)滑臊。這時候口芍,這個數(shù)據(jù)的最新版本的row trx_id是102,而90這個版本已經(jīng)成為了歷史版本雇卷。
第二個有效更新是事務B鬓椭,把數(shù)據(jù)從(1,2)改成了(1,3)。這時候关划,這個數(shù)據(jù)的最新版本(即row trx_id)是101小染,而102又成為了歷史版本。
在事務A查詢的時候祭玉,事務B還沒有提交氧映,但是它生成的(1,3)這個版本已經(jīng)變成當前版本了春畔。但這個版本對事務A必須是不可見的脱货,否則就變成臟讀了。
事務A讀數(shù)據(jù)時律姨,它的視圖數(shù)組是[99,100]振峻。當然了,讀數(shù)據(jù)都是從當前版本讀起的择份。所以扣孟,事務A查詢語句的讀數(shù)據(jù)流程是這樣的:
找到(1,3)的時候,判斷出row trx_id=101荣赶,比高水位大凤价,處于紅色區(qū)域,不可見拔创;
接著利诺,找到上一個歷史版本,一看row trx_id=102剩燥,比高水位大慢逾,處于紅色區(qū)域,不可見灭红;
再往前找侣滩,終于找到了(1,1),它的row trx_id=90变擒,比低水位小君珠,處于綠色區(qū)域,可見娇斑。所以策添,k=1澈段;
這樣執(zhí)行下來,雖然期間這一行數(shù)據(jù)被修改過舰攒,但是事務A不論在什么時候查詢败富,看到這行數(shù)據(jù)的結果都是一致的,所以我們稱之為一致性讀摩窃。
方法二:
一個數(shù)據(jù)版本兽叮,對于一個事務視圖來說,除了自己的更新總是可見以外猾愿,有三種情況:
a.版本未提交鹦聪,不可見;
b.版本已提交蒂秘,但是是在視圖創(chuàng)建后提交的泽本,不可見;
c.版本已提交姻僧,而且是在視圖創(chuàng)建前提交的规丽,可見。
所以撇贺,根據(jù)這個規(guī)則判斷圖的查詢結果赌莺,事務A的查詢語句的視圖數(shù)組是在事務A啟動的時候生成的,這時候:
(1,3)還沒提交松嘶,屬于情況1艘狭,不可見;
(1,2)雖然提交了翠订,但是是在視圖數(shù)組創(chuàng)建之后提交的巢音,屬于情況2,不可見尽超;
(1,1)是在視圖數(shù)組創(chuàng)建之前提交的官撼,可見。所以橙弱。k=1歧寺。
注意:更新數(shù)據(jù)(update)都是先讀后寫的,而這個讀棘脐,只能讀當前的值斜筐,稱為“當前讀”(current read)。(同時蛀缝,select語句如果加鎖顷链,也是當前讀。)也就是說屈梁,在更新數(shù)據(jù)語句之前嗤练,添加了一條查詢語句榛了,而且這條查詢語句直接查詢的是當前的值,不管能否可見煞抬。所以霜大,事務B得到的k=3,而不是2。
六革答、事務的可重復讀的能力的實現(xiàn)方法:
? ??????可重復讀的核心就是一致性讀(consistent read)战坤;而事務更新數(shù)據(jù)的時候,只能用當前讀残拐。如果當前的記錄的行鎖被其他事務占用的話途茫,就需要進入鎖等待。而讀提交的邏輯和可重復讀的邏輯類似溪食,它們最主要的區(qū)別是:
a.在可重復讀隔離級別下囊卜,只需要在事務開始的時候創(chuàng)建一致性視圖,之后事務里的其他查詢都共用這個一致性視圖错沃;
b.在讀提交隔離級別下栅组,每一個語句執(zhí)行前都會重新算出一個新的視圖。
c.對于可重復讀捎废,查詢只承認在事務啟動前就已經(jīng)提交完成的數(shù)據(jù)笑窜;
d.對于讀提交致燥,查詢只承認在語句啟動前就已經(jīng)提交完成的數(shù)據(jù)登疗;