国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

MySQL - 第11節(jié) - MySQL事務(wù)管理

這篇具有很好參考價值的文章主要介紹了MySQL - 第11節(jié) - MySQL事務(wù)管理。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

目錄

1.事務(wù)的概念

2.事務(wù)的版本支持

3.事務(wù)的提交方式

3.1.查看事務(wù)的提交方式

3.2.設(shè)置事務(wù)的提交方式

4.事務(wù)的相關(guān)演示

4.1.演示一:證明事務(wù)的開始與回滾

4.2.演示二:原子性

4.3.演示三:持久性

4.4.演示四:begin會自動更改提交方式

4.5.演示五:單條SQL與事務(wù)的關(guān)系

5.事務(wù)的隔離級別

5.1.理解隔離性

5.2.查看與設(shè)置隔離級別

5.3.讀未提交(Read Uncommitted)

5.4.讀提交(Read Committed)

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

5.6.串行化(Serializable)

5.7.隔離級別總結(jié)

6.關(guān)于一致性

7.多版本并發(fā)控制

7.1.數(shù)據(jù)庫的并發(fā)場景

7.2.多版本并發(fā)控制概念

7.3.記錄中的3個隱藏字段

7.4.undo日志

7.5.模擬MVCC和快照的概念

7.6.Read View

8.可重復(fù)讀(RR)與讀提交(RC)的本質(zhì)區(qū)別


1.事務(wù)的概念

事務(wù)的概念:

? 上層看起來比較簡單的需求,可能對應(yīng)的后端要做很多工作,后端這些工作組合起來才是一個完整的需求解決的方案。

??事務(wù)由一條或多條SQL語句組成,這些語句在邏輯上存在相關(guān)性,共同完成一個任務(wù),事務(wù)主要用于處理操作量大,復(fù)雜度高的數(shù)據(jù)。比如轉(zhuǎn)賬就涉及多條SQL語句,包括查詢余額(select)、在當前賬戶上減去指定金額(update)、在指定賬戶上加上對應(yīng)金額(update)等,將這多條SQL語句打包便構(gòu)成了一個事務(wù)。
??MySQL同一時刻可能存在大量事務(wù),如果不對這些事務(wù)加以控制,在執(zhí)行時就可能會出現(xiàn)問題。比如單個事務(wù)內(nèi)部的某些SQL語句執(zhí)行失敗,或是多個事務(wù)同時訪問同一份數(shù)據(jù)導(dǎo)致數(shù)據(jù)不一致的問題。

因此一個完整的事務(wù)并不是簡單的SQL集合,事務(wù)還需要滿足如下四個屬性:

??原子性: 一個事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會結(jié)束在中間某個環(huán)節(jié)。事務(wù)在執(zhí)行過程中如果發(fā)生錯誤,則會自動回滾到事務(wù)開始前的狀態(tài),就像這個事務(wù)從來沒有執(zhí)行過一樣。
??持久性: 事務(wù)處理結(jié)束后,對數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會丟失。
??隔離性: 數(shù)據(jù)庫允許多個事務(wù)同時訪問同一份數(shù)據(jù),隔離性可以保證多個事務(wù)在并發(fā)執(zhí)行時,不會因為由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致(事務(wù)由多個SQL構(gòu)成,隔離性是防止多個事務(wù)的SQL交替執(zhí)行而導(dǎo)致的數(shù)據(jù)不一致問題)。

事務(wù)隔離分為不同級別,包括讀未提交( Read uncommitted )、讀提交( read committed )、可重復(fù)讀( repeatable read )和串行化( Serializable )。

??一致性: 在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整型沒有被破壞,這表示寫入的資料必須完全符合所有的預(yù)設(shè)規(guī)則,這包含資料的精確度、串聯(lián)型以及后續(xù)數(shù)據(jù)庫可以自發(fā)性地完成預(yù)定的工作。

原子性、持久性、隔離性是手段,一致性是目的。一致性其實是僅停留在上層概念上的,也就是說原子性、持久性、隔離性都需要在MySQL內(nèi)部實現(xiàn),一致性不需要在MySQL內(nèi)部實現(xiàn),只要將原子性、持久性、隔離性這三點在MySQL內(nèi)部實現(xiàn)了,那么結(jié)果就是一致的,也就實現(xiàn)了一致性。

上面的四個屬性簡稱ACID:

??原子性(Atomicity,又稱不可分割性)。
??一致性(Consistency)。
??隔離性(Isolation,又稱獨立性)。
??持久性(Durability)。

注:MySQL要提供事務(wù)機制,注定了MySQL內(nèi)部編碼和數(shù)據(jù)結(jié)構(gòu)的支持。MySQL一定會同時存在多個事務(wù),MySQL要對多個事務(wù)進行管理工作,需要先描述再組織,因此事務(wù)不要抽象的理解它,事務(wù)最終一定是要以某種數(shù)據(jù)結(jié)構(gòu)+算法管理起來。

為什么會出現(xiàn)事務(wù):

? 事務(wù)本身不是數(shù)據(jù)庫類軟件天然有的,事務(wù)被MySQL編寫者設(shè)計出來,本質(zhì)是為了當應(yīng)用程序訪問數(shù)據(jù)庫的時候,事務(wù)能夠簡化我們的編程模型,不需要用戶自己去考慮各種各樣的潛在錯誤和并發(fā)問題。
??如果MySQL只是單純的提供數(shù)據(jù)存儲服務(wù),那么用戶在訪問數(shù)據(jù)庫時就需要自行考慮各種潛在問題,包括網(wǎng)絡(luò)異常、服務(wù)器宕機等。因此事務(wù)本質(zhì)是為了應(yīng)用服務(wù)的,而不是伴隨著數(shù)據(jù)庫系統(tǒng)天生就有的。


2.事務(wù)的版本支持

事務(wù)的版本支持:

??在MySQL中只有使用了Innodb數(shù)據(jù)庫引擎的數(shù)據(jù)庫或表才支持事務(wù),MyISAM不支持。

通過 show engines 命令可以查看數(shù)據(jù)庫引擎。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:

??Engine: 表示存儲引擎的名稱。
??Support: 表示服務(wù)器對存儲引擎的支持級別,YES表示支持,NO表示不支持,DEFAULT表示數(shù)據(jù)庫默認使用的存儲引擎,DISABLED表示支持引擎但已將其禁用。
??Comment: 表示存儲引擎的簡要說明。
??Transactions: 表示存儲引擎是否支持事務(wù),可以看到InnoDB存儲引擎支持事務(wù),而MyISAM存儲引擎不支持事務(wù)。
??XA: 表示存儲引擎是否支持XA事務(wù)。
??Savepoints: 表示存儲引擎是否支持保存點。


3.事務(wù)的提交方式

3.1.查看事務(wù)的提交方式

查看事務(wù)的提交方式:

事務(wù)常見的提交方式有兩種,分別是自動提交和手動提交。

通過show命令查看autocommit全局變量,可以查看事務(wù)的自動提交是否被打開。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:autocommit的值為ON表示自動提交被打開,值為OFF表示自動提交被關(guān)閉,即事務(wù)的提交方式為手動提交。

3.2.設(shè)置事務(wù)的提交方式

設(shè)置事務(wù)的提交方式:

通過set命令設(shè)置autocommit全局變量的值,可以打開或關(guān)閉事務(wù)的自動提交。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:將autocommit的值設(shè)置為1表示打開自動提交,設(shè)置為0表示關(guān)閉自動提交,相當于將事務(wù)提交方式設(shè)置為手動提交。?


4.事務(wù)的相關(guān)演示

準備測試表:

為了便于演示,我們將MySQL的隔離級別設(shè)置成讀未提交,也就是把隔離級別設(shè)置的比較低,方便看到實驗現(xiàn)象。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

需要注意的是,設(shè)置全局隔離級別后當前會話的隔離級別不會改變,只會影響后續(xù)與MySQL新建立的連接,因此需要重啟終端才能看到會話的隔離級別被成功設(shè)置。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

創(chuàng)建一個銀行用戶表,表中包含用戶的id、姓名和賬戶余額。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

4.1.演示一:證明事務(wù)的開始與回滾

演示一:證明事務(wù)的開始與回滾

啟動兩個終端,左終端使用 begin 或 start transaction 命令啟動一個事務(wù),右終端查看銀行用戶表中的信息。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

左終端中的事務(wù)向表中插入一條記錄,由于我們將隔離級別設(shè)置成了讀未提交,因此在左終端中的事務(wù)使用commit提交之前,在右終端中就能查看到事務(wù)向表中插入的記錄。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

左終端中的事務(wù)使用savepoint命令創(chuàng)建一個保存點,然后繼續(xù)向表中插入一條記錄,這時在右終端中也能看到新插入的這條記錄。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

左終端中的事務(wù)使用rollback命令回滾到保存點,這時右終端在查看表中數(shù)據(jù)時就看不到剛才插入的第二條記錄了。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

左終端中的事務(wù)使用rollback命令回滾到事務(wù)最開始,這時右終端在查看表中數(shù)據(jù)時就看不到任何記錄了。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

最后結(jié)束事務(wù),使用 commit 命令提交事務(wù),如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:

??使用 begin 或 start transaction 命令,可以啟動一個事務(wù)。
??使用 savepoint 保存點 命令,可以在事務(wù)中創(chuàng)建指定名稱的保存點。
??使用 rollback to 保存點 命令,可以讓事務(wù)回滾到指定保存點。
??使用 rollback 命令,可以直接讓事務(wù)回滾到最開始。
??使用 commit 命令,可以提交事務(wù),提交事務(wù)后就不能回滾了。

注:

1.先設(shè)置s1保存點,然后進行一些操作后設(shè)置s2保存點,如果此時回滾到s1保存點,那么就無法回滾到s2保存點了,因為回滾到s1保存點后,s1保存點后面的操作都不存在了(包括設(shè)置s2保存點的操作)。

2.如果沒有設(shè)置保存點,也可以直接使用 rollback 回滾(前提是事務(wù)還沒有提交),但只能回滾到事務(wù)的開始。如果一個事務(wù)被提交了(commit),則不可以回退(rollback)。

3.事務(wù)中所謂的commit提交,并不是將數(shù)據(jù)刷新到磁盤中,刷新到磁盤的過程是mysqld自己執(zhí)行的,commit提交其實是將對應(yīng)數(shù)據(jù)交付給了mysqld。

MySQL采用兩段式提交:第一次是commit提交將數(shù)據(jù)交付給mysqld,第二次是mysqld按照一定規(guī)則將數(shù)據(jù)刷新到磁盤中。

4.2.演示二:原子性

演示二:原子性

在左終端中啟動一個事務(wù),在右終端查看銀行用戶表中的信息。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

左終端中的事務(wù)向表中插入一條記錄,由于隔離級別是讀未提交,因此在右終端中能夠查詢到插入的這條記錄。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

如果左終端中的事務(wù)在提交之前因為某些原因與MySQL斷開連接,這里可以使用 quit 命令退出或使用 ctrl + \ 命令讓程序崩潰,那么MySQL會自動讓事務(wù)回滾到最開始,這時右終端中就看不到之前插入的記錄了。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

因此,事務(wù)可以手動回滾,同時,當操作異常,MySQL會自動回滾。

4.3.演示三:持久性

演示三:持久性

在左終端中啟動一個事務(wù),在右終端查看銀行用戶表中的信息。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

左終端中的事務(wù)向表中插入一條記錄,由于隔離級別是讀未提交,因此在右終端中能夠查詢到插入的這條記錄。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

左終端中的事務(wù)在commit提交后與MySQL斷開連接,這時右終端中仍然可以看到之前插入的記錄,因為事務(wù)提交后數(shù)據(jù)就被持久化了。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

4.4.演示四:begin會自動更改提交方式

演示四:begin會自動更改提交方式

通過show命令查看autocommit的值為ON,表示事務(wù)的提交方式是自動提交,此時銀行用戶表中有一條記錄。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

在左終端中啟動一個事務(wù)并向表中新插入一條記錄,由于隔離級別是讀未提交,因此在右終端中能夠查詢到新插入的這條記錄。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

如果左終端中的事務(wù)在提交之前與MySQL斷開連接,那么MySQL依舊會自動讓事務(wù)回滾到最開始,這時右終端中就看不到之前新插入的記錄了。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

也就是說,使用begin或start transaction命令啟動的事務(wù),都必須要使用commit命令手動提交,數(shù)據(jù)才會被持久化,與是否設(shè)置autocommit無關(guān)。

因此在begin(start transaction)和commit之間的提交方式一定為手動提交,即begin會自動更改提交方式為手動提交。

4.5.演示五:單條SQL與事務(wù)的關(guān)系

演示五:單條SQL與事務(wù)的關(guān)系

??實際全局變量autocommit是否被設(shè)置影響的是單條SQL語句,InnoDB中的每一條SQL都會默認被封裝成事務(wù)(只不過該事務(wù)內(nèi)只有一條SQL語句)。
??autocommit為ON,則單條SQL語句執(zhí)行后會自動被提交,如果為OFF,則SQL語句執(zhí)行后需要使用commit進行手動提交。
比如通過show命令查看autocommit的值為ON,表示事務(wù)的提交方式是自動提交,此時銀行用戶表中有一條記錄。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

在左終端中直接向表中新插入一條記錄,由于隔離級別是讀未提交,因此在右終端中肯定能夠查詢到新插入的這條記錄。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

但就算左終端在執(zhí)行單條SQL后不使用commit進行提交,而直接與MySQL斷開連接,這時右終端仍然可以看到之前新插入的記錄了,因為單條SQL在執(zhí)行后被自動提交持久化了。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

相反,如果將autocommit設(shè)置為OFF,表示事務(wù)執(zhí)行后需要手動提交,此時銀行用戶表中有兩條記錄。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

在左終端中直接向表中新插入一條記錄,由于隔離級別是讀未提交,因此在右終端中肯定能夠查詢到新插入的這條記錄。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

但如果此時左終端在執(zhí)行單條SQL后不使用commit進行提交,而直接與MySQL斷開連接,那么這時右終端中就看不到之前新插入的記錄了,因為這時單條SQL執(zhí)行后需要使用commit手動提交后才會持久化,在commit之前與MySQL斷開連接則會自動進行回滾操作。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

也就是說,實際我們之前一直都在使用單SQL事務(wù),只不過autocommit默認是打開的,因此單SQL事務(wù)執(zhí)行后自動就被提交了。


5.事務(wù)的隔離級別

5.1.理解隔離性

理解隔離性:

??MySQL服務(wù)可能會同時被多個客戶端進程(線程)訪問,訪問的方式以事務(wù)的方式進行。
??一個事務(wù)可能由多條SQL語句構(gòu)成,也就意味著任何一個事務(wù),都有執(zhí)行前、執(zhí)行中和執(zhí)行后三個階段,而所謂的原子性就是讓用戶層要么看到執(zhí)行前,要么看到執(zhí)行后,執(zhí)行中如果出現(xiàn)問題,可以隨時進行回滾,所以單個事務(wù)對用戶表現(xiàn)出來的特性就是原子性。
??但畢竟每個事務(wù)都有一個執(zhí)行的過程,在多個事務(wù)各自執(zhí)行自己的多條SQL時,仍然可能會出現(xiàn)互相影響的情況,比如多個事務(wù)同時訪問同一張表,甚至是表中的同一條記錄。
??數(shù)據(jù)庫為了保證事務(wù)執(zhí)行過程中盡量不受干擾,于是出現(xiàn)了隔離性的概念,而數(shù)據(jù)庫為了允許事務(wù)在執(zhí)行過程中受到不同程度的干擾,于是出現(xiàn)了隔離級別的概念。

數(shù)據(jù)庫事務(wù)的隔離級別有以下四種:

??讀未提交(Read Uncommitted): 在該隔離級別下,所有的事務(wù)都可以看到其他事務(wù)沒有提交的執(zhí)行結(jié)果,實際生產(chǎn)中不可能使用這種隔離級別,因為這種隔離級別相當于沒有任何隔離性,會存在很多并發(fā)問題,如臟讀、幻讀、不可重復(fù)讀等。
??讀提交(Read Committed): 該隔離級別是大多數(shù)數(shù)據(jù)庫的默認隔離級別,但它不是MySQL默認的隔離級別,它滿足了隔離的簡單定義:一個事務(wù)只能看到其他已經(jīng)提交的事務(wù)所做的改變,但這種隔離級別存在不可重復(fù)讀和幻讀的問題。
??可重復(fù)讀(Repeatable Read): 這是MySQL默認的隔離級別,該隔離級別確保同一個事務(wù)在執(zhí)行過程中,多次讀取操作數(shù)據(jù)時會看到同樣的數(shù)據(jù),即解決了不可重復(fù)讀的問題,但這種隔離級別下仍然存在幻讀的問題。
??串行化(Serializable): 這是事務(wù)的最高隔離級別,該隔離級別通過強制事務(wù)排序,使之不可能相互沖突,從而解決了幻讀問題。它在每個讀的數(shù)據(jù)行上面加上共享鎖,但是可能會導(dǎo)致超時和鎖競爭問題,這種隔離級別太極端,實際生成中基本不使用。

注:

1.隔離級別(隔離性):讀未提交<讀提交<可重復(fù)讀<串行化。

2.雖然數(shù)據(jù)庫事務(wù)的隔離級別有以上四種,但一個穩(wěn)態(tài)的數(shù)據(jù)庫只會選擇這其中的一種,作為自己的默認隔離級別。但數(shù)據(jù)庫默認的隔離級別有時可能并不滿足上層的業(yè)務(wù)需求,因此數(shù)據(jù)庫提供了這四種隔離級別,可以讓我們自行設(shè)置。
3.隔離級別基本上都是通過加鎖的方式實現(xiàn)的,不同的隔離級別對鎖的使用是不同的,常見的有表鎖、行鎖、寫鎖、間隙鎖(GAP)、Next-Key鎖(GAP+行鎖)等。

5.2.查看與設(shè)置隔離級別

查看全局隔離級別:

通過 select @@global.tx_isolation 命令,可以查看全局隔離級別。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

查看會話隔離級別:

通過 select @@session.tx_isolation 命令,可以查看當前會話的隔離級別。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

此外,通過 select @@tx_isolation 命令,也可以查看當前會話的隔離級別。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

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

通過 set session transaction isolation level 隔離級別 命令,可以設(shè)置當前會話的隔離級別。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

注:設(shè)置會話的隔離級別只會影響當前會話,新起的會話依舊采用全局隔離級。

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

通過 set global transaction isolation level 隔離級別 命令,可以設(shè)置全局隔離級別。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

注:

1.設(shè)置全局隔離級別會影響后續(xù)的新會話,但當前會話的隔離級別沒有發(fā)生變化,如果要讓當前會話的隔離級別也改變,則需要重啟會話。?

2.設(shè)置完全局隔離級別并重啟后,數(shù)據(jù)庫中所有的會話隔離級別都會重新讀取并初始化為該全局隔離級別,相當于將該全局隔離級別作用在了數(shù)據(jù)庫的所有會話中。

3.如果使用?systemctl restart mysqld 命令將MySQL數(shù)據(jù)庫重啟,那么數(shù)據(jù)庫會重新從配置文件中讀取默認的全局隔離級別將原本的全局隔離級別覆蓋,進而數(shù)據(jù)庫中所有的會話隔離級別重新讀取該默認的全局隔離級別,并作用在了數(shù)據(jù)庫的所有會話中。

5.3.讀未提交(Read Uncommitted)

啟動兩個終端,將隔離級別都設(shè)置為讀未提交,并查看此時銀行用戶表中的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

在兩個終端各自啟動一個事務(wù),左終端中的事務(wù)所作的修改在沒有提交之前,右終端中的事務(wù)就已經(jīng)能夠看到了。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:

??讀未提交是事務(wù)的最低隔離級別,幾乎沒有加鎖,雖然效率高,但是問題比較多,所以嚴重不建議使用。

??一個事務(wù)在執(zhí)行過程中,讀取到另一個執(zhí)行中的事務(wù)所做的修改,但是該事務(wù)還沒有進行提交,這種現(xiàn)象叫做臟讀。

5.4.讀提交(Read Committed)

啟動兩個終端,將隔離級別都設(shè)置為讀提交,并查看此時銀行用戶表中的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

在兩個終端各自啟動一個事務(wù),左終端中的事務(wù)所作的修改在沒有提交之前,右終端中的事務(wù)無法看到。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

只有當左終端中的事務(wù)提交后,右終端中的事務(wù)才能看到修改后的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:

??一個事務(wù)在執(zhí)行過程中,兩個相同的select查詢得到了不同的數(shù)據(jù),這種現(xiàn)象叫做不可重復(fù)讀。不可重復(fù)讀現(xiàn)象會使得同一個事務(wù)在select讀取時,因為select讀取的時間點不同而讀到不同的內(nèi)容。

??不可重復(fù)讀現(xiàn)象是有問題的,下面舉例進行解釋。

公司年終要根據(jù)不同工資區(qū)間的員工發(fā)放不同的年終禮品,因此程序員A要根據(jù)公司不同的工資區(qū)間對員工進行篩選,當程序員A啟動一個事務(wù)正在篩選的同時,程序員B迅速將自己的工資做了調(diào)整,由工資區(qū)間1跳到了工資區(qū)間2,并commit提交。假設(shè)程序員B調(diào)整前,程序員A篩選了工資區(qū)間1的員工,里面包含程序員B,程序員B調(diào)整后,程序員A剛好準備篩選工資區(qū)間2的員工,此時里面仍然包含程序員B,這樣就出現(xiàn)了問題。

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

啟動兩個終端,將隔離級別都設(shè)置為可重復(fù)讀,并查看此時銀行用戶表中的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

在兩個終端各自啟動一個事務(wù),左終端中的事務(wù)所作的修改在沒有提交之前,右終端中的事務(wù)無法看到。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

并且當左終端中的事務(wù)提交后,右終端中的事務(wù)仍然看不到修改后的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

只有當右終端中的事務(wù)提交后再查看表中的數(shù)據(jù),這時才能看到修改后的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:

??在可重復(fù)讀隔離級別下,一個事務(wù)在執(zhí)行過程中,相同的select查詢得到的是相同的數(shù)據(jù),這就是所謂的可重復(fù)讀。
??一般的數(shù)據(jù)庫(這里不包括MySQL數(shù)據(jù)庫)在可重復(fù)讀隔離級別下,update、insert、delete數(shù)據(jù)是滿足可重復(fù)讀的,但insert數(shù)據(jù)會存在幻讀問題,因為隔離性是通過對數(shù)據(jù)加鎖完成的,而新插入的數(shù)據(jù)原本是不存在的,因此一般的加鎖無法屏蔽這類問題。MySQL數(shù)據(jù)庫的可重復(fù)讀隔離級別下不存在幻讀問題,其原理后面會講。
??一個事務(wù)在執(zhí)行過程中,相同的select查詢多次查詢得到了新的數(shù)據(jù),如同出現(xiàn)了幻覺,這種現(xiàn)象叫做幻讀。
MySQL解決了可重復(fù)讀隔離級別下的幻讀問題,比如重新在這兩個終端各自啟動一個事務(wù),左終端中的事務(wù)向表中插入數(shù)據(jù)的在沒有提交之前,右終端中的事務(wù)無法看到。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

并且當左終端中的事務(wù)提交后,右終端中的事務(wù)仍然看不到新插入的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

只有當右終端中的事務(wù)提交后再查看表中的數(shù)據(jù),這時才能看到新插入的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:

??MySQL是通過Next-Key鎖(GAP+行鎖)來解決幻讀問題的。

5.6.串行化(Serializable)

啟動兩個終端,將隔離級別都設(shè)置為串行化,并查看此時銀行用戶表中的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

在兩個終端各自啟動一個事務(wù),如果這兩個事務(wù)都對表進行的是讀操作,那么這兩個事務(wù)可以并發(fā)執(zhí)行,不會被阻塞。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

但如果這兩個事務(wù)中有一個事務(wù)要對表進行寫操作,那么這個事務(wù)就會立即被阻塞。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

直到訪問這張表的其他事務(wù)都提交后,這個被阻塞的事務(wù)才會被喚醒,然后才能對表進行修改操作。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:

??串行化是事務(wù)的最高隔離級別,多個事務(wù)同時進行讀操作時加的是共享鎖,因此可以并發(fā)執(zhí)行讀操作,但一旦需要進行寫操作,就會進行串行化,效率很低,幾乎不會使用。

5.7.隔離級別總結(jié)

對MySQL中的隔離級別總結(jié)如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

√:會發(fā)生該問題? ? ? ? ? X:不會發(fā)生該問題

說明一下:

??隔離級別越嚴格,安全性越高,但數(shù)據(jù)庫的并發(fā)性能也就越低,在選擇隔離級別時往往需要在兩者之間找一個平衡點。

?? 不可重復(fù)讀的重點是修改和刪除:同樣的條件, 你讀取過的數(shù)據(jù) , 再次讀取出來發(fā)現(xiàn)值不一樣了。幻讀的重點在于新增:同樣的條件, 1 次和第 2 次讀出來的記錄數(shù)不一樣。

??表中只寫出了各種隔離級別下進行讀操作時是否需要加鎖,因為無論哪種隔離級別,只要需要進行寫操作就一定需要加鎖。

?? 上面的例子可以看出,事務(wù)也有長短事務(wù)這樣的概念。事務(wù)間互相影響,指的是事務(wù)在并行執(zhí)行的時候,即都沒有commit 的時候,影響會比較大。

??mysql 默認的隔離級別是可重復(fù)讀一般情況下不要修改。


6.關(guān)于一致性

事務(wù)執(zhí)行的結(jié)果,必須使數(shù)據(jù)庫從一個一致性狀態(tài),變到另一個一致性狀態(tài),當數(shù)據(jù)庫只包含事務(wù)成功提交的結(jié)果時,數(shù)據(jù)庫就處于一致性狀態(tài)。

??事務(wù)在執(zhí)行過程中如果發(fā)生錯誤,則需要自動回滾到事務(wù)最開始的狀態(tài),就像這個事務(wù)從來沒有執(zhí)行過一樣,即一致性需要原子性來保證。
??事務(wù)處理結(jié)束后,對數(shù)據(jù)的修改必須是永久的,即便系統(tǒng)故障也不能丟失,即一致性需要持久性來保證。
??多個事務(wù)同時訪問同一份數(shù)據(jù)時,必須保證這多個事務(wù)在并發(fā)執(zhí)行時,不會因為由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致,即一致性需要隔離性來保證。
??此外,一致性與用戶的業(yè)務(wù)邏輯強相關(guān),如果用戶本身的業(yè)務(wù)邏輯有問題,最終也會讓數(shù)據(jù)庫處于一種不一致的狀態(tài)。
也就是說,一致性實際是數(shù)據(jù)庫最終要達到的效果,一致性不僅需要原子性、持久性和隔離性來保證,還需要上層用戶編寫出正確的業(yè)務(wù)邏輯。


7.多版本并發(fā)控制

7.1.數(shù)據(jù)庫的并發(fā)場景

數(shù)據(jù)庫并發(fā)的場景無非如下三種:

??讀-讀并發(fā):不存在任何問題,也不需要并發(fā)控制。
??讀-寫并發(fā):有線程安全問題,可能會存在事務(wù)隔離性問題,可能遇到臟讀、幻讀、不可重復(fù)讀。
??寫-寫并發(fā):有線程安全問題,可能會存在兩類更新丟失問題。
說明一下:

??寫-寫并發(fā)場景下的第一類更新丟失又叫做回滾丟失,即一個事務(wù)的回滾把另一個已經(jīng)提交的事務(wù)更新的數(shù)據(jù)覆蓋了,第二類更新丟失又叫做覆蓋丟失,即一個事務(wù)的提交把另一個已經(jīng)提交的事務(wù)更新的數(shù)據(jù)覆蓋了。
??讀-讀并發(fā)不需要進行并發(fā)控制,寫-寫并發(fā)實際也就是對數(shù)據(jù)進行加鎖,這里最值得討論的是讀-寫并發(fā),讀-寫并發(fā)是數(shù)據(jù)庫當中最高頻的場景,在解決讀-寫并發(fā)時不僅需要考慮線程安全問題,還需要考慮并發(fā)的性能問題。

7.2.多版本并發(fā)控制概念

??多版本并發(fā)控制(Multi-Version Concurrency Control,MVCC)是一種用來解決讀寫沖突的無鎖并發(fā)控制,主要依賴記錄中的3個隱藏字段、undo日志和Read View實現(xiàn)。
??為事務(wù)分配單向增長的事務(wù)ID,為每個修改保存一個版本,將版本與事務(wù)ID相關(guān)聯(lián),讀操作只讀該事務(wù)開始前的數(shù)據(jù)庫快照。
??MVCC保證讀寫并發(fā)時,讀操作不會阻塞寫操作,寫操作也不會阻塞讀操作,提高了數(shù)據(jù)庫并發(fā)讀寫的性能,同時還可以解決臟讀、幻讀和不可重復(fù)讀等事務(wù)隔離性問題。

7.3.記錄中的3個隱藏字段

數(shù)據(jù)庫表中的每條記錄都會有如下3個隱藏字段:

??DB_TRX_ID:6字節(jié),創(chuàng)建(插入)或最近一次修改該記錄的事務(wù)ID。
??DB_ROW_ID:6字節(jié),隱含的自增ID(隱藏主鍵)。
??DB_ROLL_PTR:7字節(jié),回滾指針,指向這條記錄的上一個版本。
說明一下:

??采用InnoDB存儲引擎建立的每張表都會有一個主鍵,如果用戶沒有設(shè)置,InnoDB就會自動以DB_ROW_ID產(chǎn)生一個聚簇索引。
??此外,數(shù)據(jù)庫表中的每條記錄還有一個刪除flag隱藏字段,用于表示該條記錄是否被刪除,便于進行數(shù)據(jù)回滾。

示例:

創(chuàng)建一個學(xué)生表,表中包含學(xué)生的姓名和年齡。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

當向表中插入一條記錄后,該記錄不僅包含name和age字段,還包含三個隱藏字段。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:

??假設(shè)插入該記錄的事務(wù)的事務(wù)ID為9,那么該記錄的DB_TRX_ID字段填的就是9。
??因為這是插入的第一條記錄,所以隱式主鍵DB_ROW_ID字段填的就是1。
??由于這條記錄是新插入的,沒有歷史版本,所以回滾指針DB_ROLL_PTR的值設(shè)置為null。
??MVCC重點需要的就是這三個隱藏字段,實際還有其他隱藏字段,只不過沒有畫出。

??這里只插入展示了一條記錄,如果有很多記錄,那么每條記錄的后面都會跟這些隱藏字段。

7.4.undo日志

MySQL的三大日志如下:

??redo log:重做日志,用于MySQL崩潰后進行數(shù)據(jù)恢復(fù),保證數(shù)據(jù)的持久性。
??bin log:邏輯日志,用于主從數(shù)據(jù)備份時進行數(shù)據(jù)同步,保證數(shù)據(jù)的一致性。
??undo log:回滾日志,用于對已經(jīng)執(zhí)行的操作進行回滾,保證事務(wù)的原子性。
MySQL會為上述三大日志開辟對應(yīng)的緩沖區(qū)(一段內(nèi)存空間,與buffer pool并列),用于存儲日志相關(guān)的信息,必要時會將緩沖區(qū)中的數(shù)據(jù)刷新到磁盤。

說明一下:

??MVCC的實現(xiàn)主要依賴三大日志中的undo log,記錄的歷史版本就是存儲在undo log對應(yīng)的緩沖區(qū)中的。

??不要僅僅認為日志這個東西就是執(zhí)行記錄,MySQL的日志是具有功能性和數(shù)據(jù)/sql保存的能力的。

7.5.模擬MVCC和快照的概念

現(xiàn)在有一個事務(wù)ID為10的事務(wù),要將剛才插入學(xué)生表中的記錄的學(xué)生姓名改為“李四”:

??因為是要進行寫操作,所以需要先給該記錄加行鎖。
??修改前,先將該行記錄拷貝到undo log中,此時undo log中就有了一行副本數(shù)據(jù)。
??然后再將原始記錄中的學(xué)生姓名改為“李四”,并將該記錄的DB_TRX_ID改為10,回滾指針DB_ROLL_PTR設(shè)置成undo log中副本數(shù)據(jù)的地址,從而指向該記錄的上一個版本。
??最后當事務(wù)10提交后釋放鎖,這時最新的記錄就是學(xué)生姓名為“李四”的那條記錄。
修改后的示意圖如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

現(xiàn)在又有一個事務(wù)ID為11的事務(wù),要將剛才學(xué)生表中的那條記錄的學(xué)生年齡改為38:

??因為是要進行寫操作,所以需要先給該記錄(最新的記錄)加行鎖。
??修改前,先將該行記錄拷貝到undo log中,此時undo log中就又有了一行副本數(shù)據(jù)。
??然后再將原始記錄中的學(xué)生年齡改為38,并將該記錄的DB_TRX_ID改為11,回滾指針DB_ROLL_PTR設(shè)置成剛才拷貝到undo log中的副本數(shù)據(jù)的地址,從而指向該記錄的上一個版本。
??最后當事務(wù)11提交后釋放鎖,這時最新的記錄就是學(xué)生年齡為38的那條記錄。
修改后的示意圖如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

此時我們就有了一個基于鏈表記錄的歷史版本鏈,而undo log中的一個個的歷史版本就稱為一個個的快照。

說明一下:

??所謂的回滾實際就是用undo log中的歷史數(shù)據(jù)覆蓋當前數(shù)據(jù),而所謂的創(chuàng)建保存點就可以理解成是給某些版本做了標記,讓我們可以直接用這些版本數(shù)據(jù)來覆蓋當前數(shù)據(jù)。
??這種技術(shù)實際就是基于版本的寫時拷貝,當需要進行寫操作時先將最新版本拷貝一份到undo log中,然后再進行寫操作,和父子進程為了保證獨立性而進行的寫時拷貝是類似的。

insert和delete的記錄如何維護版本鏈?

??刪除記錄并不是真的把數(shù)據(jù)刪除了,而是先將該記錄拷貝一份放入undo log中,然后將該記錄的刪除flag隱藏字段設(shè)置為1,這樣回滾后該記錄的刪除flag隱藏字段就又變回0了,相當于刪除的數(shù)據(jù)又恢復(fù)了。
??新插入的記錄是沒有歷史版本的,但是一般為了回滾操作,新插入的記錄也需要拷貝一份放入undo log中,只不過被拷貝到undo log中的記錄的刪除flag隱藏字段被設(shè)置為1,這樣回滾后就相當于新插入的數(shù)據(jù)就被刪除了。
也就是說,增加、刪除和修改數(shù)據(jù)都是可以形成版本鏈的。

當前讀 VS 快照讀:

??當前讀:讀取最新的記錄,就叫做當前讀。
??快照讀:讀取歷史版本,就叫做快照讀。

事務(wù)在進行增刪查改的時候,并不是都需要進行加鎖保護:

??事務(wù)對數(shù)據(jù)進行增刪改的時候,操作的都是最新記錄,即當前讀,需要進行加鎖保護。
??事務(wù)在進行select查詢的時候,既可能是當前讀也可能是快照讀,如果是當前讀,那也需要進行加鎖保護,但如果是快照讀,那就不需要加鎖,因為歷史版本不會被修改,也就是可以并發(fā)執(zhí)行(讀-寫可以并發(fā)執(zhí)行),提高了效率,這也就是MVCC的意義所在。
而select查詢時應(yīng)該進行當前讀還是快照讀,則是由隔離級別決定的,在讀未提交和串行化隔離級別下,進行的都是當前讀,而在讀提交和可重復(fù)讀隔離級別下,既可能進行當前讀也可能進行快照讀。

undo log中的版本鏈何時才會被清除?

??在undo log中形成的版本鏈不僅僅是為了進行回滾操作,其他事務(wù)在執(zhí)行過程中也可能讀取版本鏈中的某個版本,也就是快照讀。
??因此,只有當某條記錄的最新版本已經(jīng)修改并提交,并且此時沒有其他事務(wù)與該記錄的歷史版本有關(guān)了,這時當調(diào)用commit提交后,該記錄在undo log中的版本鏈才可以被清除(也就是說,當一個事務(wù)commit提交后并且沒有其他事務(wù)與該記錄的歷史版本有關(guān)了,該記錄在undo log中的版本鏈才會被清除)。

??undo log是給一個事務(wù)在其還沒有提交前所使用的。
說明一下:

??對于新插入的記錄來說,沒有其他事務(wù)會訪問它的歷史版本,因此新插入的記錄在提交后就可以將undo log中的版本鏈清除了。
??因此版本鏈在undo log中可能會存在很長時間,尤其是有其他事務(wù)和這個版本鏈相關(guān)聯(lián)的時候,但這也沒有壞處,這說明它是一個熱數(shù)據(jù)。

7.6.Read View

問題:為什么要有隔離級別呢?
答:事務(wù)都是原子的,所以無論如何,事務(wù)總有先有后。但是經(jīng)過上面的操作我們發(fā)現(xiàn),事務(wù)從 begin->CURD->commit? 是有一個階段的,也就是事務(wù)有執(zhí)行前,執(zhí)行中,執(zhí)行后的
階段。但不管怎么啟動多個事務(wù),總是有先有后的。
那么多個事務(wù)在執(zhí)行中, CURD 操作是會交織在一起的。為了保證事務(wù)的 有先有后 ,就應(yīng)該讓不同的事務(wù)看到它該看到的內(nèi)容,這就是所謂的隔離性與隔離級別要解決的問題。
先來的事務(wù),不應(yīng)該看到后來的事務(wù)所做的修改,那么如何保證不同的事務(wù)看到不同的內(nèi)容呢?也就是如何實現(xiàn)隔離級別呢? 答案是Read View。

Read View:

??事務(wù)在進行快照讀操作時會生成讀視圖Read View,在該事務(wù)執(zhí)行快照讀的那一刻,會生成數(shù)據(jù)庫系統(tǒng)當前的一個快照,記錄并維護系統(tǒng)當前活躍的事務(wù)ID(當每個事務(wù)開啟時,都會被分配一個ID,這個ID是遞增的,所以最新的事務(wù),ID值越大)(事務(wù)到來的先后就是由事務(wù)ID區(qū)分的)。

??Read View在MySQL源碼中就是一個類,本質(zhì)是用來進行可見性判斷的,當事務(wù)對某個記錄執(zhí)行快照讀的時候,對該記錄創(chuàng)建一個Read View,根據(jù)這個Read View來判斷,當前事務(wù)能夠看到該記錄的哪個版本的數(shù)據(jù)。

注:當使用begin啟動事務(wù)的時候是沒有read view的,但肯定有事務(wù)ID和事務(wù)對象,當首次進行select的時候,mysqld會自動形成read view。

ReadView類的源碼如下:

class ReadView {
	// 省略...
private:
	/** 高水位:大于等于這個ID的事務(wù)均不可見*/
	trx_id_t m_low_limit_id;
	
	/** 低水位:小于這個ID的事務(wù)均可見 */
	trx_id_t m_up_limit_id;
	
	/** 創(chuàng)建該 Read View 的事務(wù)ID*/
	trx_id_t m_creator_trx_id;
	
	/** 創(chuàng)建視圖時的活躍事務(wù)id列表*/
	ids_t m_ids;
	
	/** 配合purge,標識該視圖不需要小于m_low_limit_no的UNDO LOG,
	* 如果其他視圖也不需要,則可以刪除小于m_low_limit_no的UNDO LOG*/
	trx_id_t m_low_limit_no;
	
	/** 標記視圖是否被關(guān)閉*/
	bool m_closed;
	
	// 省略...
};

部分成員說明:

??m_ids: 一張列表,記錄Read View生成時刻,系統(tǒng)中活躍的事務(wù)ID。
??m_up_limit_id: 記錄m_ids列表中事務(wù)ID最小的ID。
??m_low_limit_id: 記錄Read View生成時刻,系統(tǒng)尚未分配的下一個事務(wù)ID(整個系統(tǒng)已經(jīng)分配的事務(wù)ID值最大值+1)。
??m_creator_trx_id: 記錄創(chuàng)建該Read View的事務(wù)的事務(wù)ID。
由于事務(wù)ID是單向增長的,因此根據(jù)Read View中的m_up_limit_id和m_low_limit_id,可以將事務(wù)ID分為三個部分:

??事務(wù)ID小于m_up_limit_id的事務(wù),一定是生成Read View時已經(jīng)提交的事務(wù),因為m_up_limit_id是生成Read View時刻系統(tǒng)中活躍事務(wù)ID中的最小ID,因此事務(wù)ID比它小的事務(wù)在生成Read View時一定已經(jīng)提交了。
??事務(wù)ID大于等于m_low_limit_id的事務(wù),一定是生成Read View時還沒有啟動的事務(wù),因為m_low_limit_id是生成Read View時刻,系統(tǒng)尚未分配的下一個事務(wù)ID。
??事務(wù)ID位于m_up_limit_id和m_low_limit_id之間的事務(wù),在生成Read View時可能正處于活躍狀態(tài),也可能已經(jīng)提交了,這時需要通過判斷事務(wù)ID是否存在于m_ids中來判斷該事務(wù)是否已經(jīng)提交。
示意圖如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

??一個事務(wù)在進行讀操作時,只應(yīng)該看到自己或已經(jīng)提交的事務(wù)所作的修改,因此我們可以根據(jù)Read View來判斷當前事務(wù)能否看到另一個事務(wù)所作的修改。
??版本鏈中的每個版本的記錄都有自己的DB_TRX_ID,即創(chuàng)建或最近一次修改該記錄的事務(wù)ID,因此可以依次遍歷版本鏈中的各個版本,通過Read View來判斷當前事務(wù)能否看到這個版本,如果不能則繼續(xù)遍歷下一個版本。

流程圖如下圖所示:

MySQL - 第11節(jié) - MySQL事務(wù)管理

注:上圖中的 creator_trx_id 是本事務(wù)ID。

源碼策略如下:

bool changes_visible(trx_id_t id, const table_name_t& name) const 
	MY_ATTRIBUTE((warn_unused_result))
{
	ut_ad(id > 0);
	//1、事務(wù)id小于m_up_limit_id(已提交)或事務(wù)id為創(chuàng)建該Read View的事務(wù)的id,則可見
	if (id < m_up_limit_id || id == m_creator_trx_id) {
		return(true);
	}
	check_trx_id_sanity(id, name);
	//2、事務(wù)id大于等于m_low_limit_id(生成Read View時還沒有啟動的事務(wù)),則不可見
	if (id >= m_low_limit_id) {
		return(false);
	}
	//3、事務(wù)id位于m_up_limit_id和m_low_limit_id之間,并且活躍事務(wù)id列表為空(即不在活躍列表中),則可見
	else if (m_ids.empty()) {
		return(true);
	}
	const ids_t::value_type* p = m_ids.data();
	//4、事務(wù)id位于m_up_limit_id和m_low_limit_id之間,如果在活躍事務(wù)id列表中則不可見,如果不在則可見
	return (!std::binary_search(p, p + m_ids.size(), id));
}

說明一下:使用該函數(shù)時將版本的DB_TRX_ID傳給參數(shù)id,該函數(shù)的作用就是根據(jù)Read View,判斷當前事務(wù)能否看到這個版本。

舉例說明:

假設(shè)當前有條記錄:
MySQL - 第11節(jié) - MySQL事務(wù)管理

事務(wù)操作: ?

事務(wù)1、2、3、4開始后,事務(wù)四首先對記錄進行修改并提交,然后事務(wù)2進行select讀取。

MySQL - 第11節(jié) - MySQL事務(wù)管理
事務(wù)4 修改 name( 張三 ) 變成 name( 李四 ) ,然后事務(wù)2 對該記錄執(zhí)行了 快照讀 ,數(shù)據(jù)庫為該行數(shù)據(jù)生成一個 Read View 讀視圖,讀視圖如下面代碼所示,此時版本鏈如下圖所示。?
//事務(wù)2的 Read View
m_ids; // 1,3
up_limit_id; // 1
low_limit_id; // 4 + 1 = 5,原因:ReadView生成時刻,系統(tǒng)尚未分配的下一個事務(wù)ID
creator_trx_id // 2
MySQL - 第11節(jié) - MySQL事務(wù)管理

因為事務(wù)4先提交,事務(wù)2再形成快照,所以,當前事務(wù)2能看到版本鏈中的哪一個,我們從DB_TRX_ID=4開始找,如下圖所示。

MySQL - 第11節(jié) - MySQL事務(wù)管理

我們的事務(wù) 2 在快照讀該行記錄的時候,就會拿該行記錄的 DB_TRX_ID 去跟 up_limit_id,low_limit_id 和活躍事務(wù)ID 列表 (trx_list) 進行比較,判斷當前事務(wù) 2 能否看到該記錄的版本,如下代碼所示。
//事務(wù)2的 Read View
m_ids; // 1,3
up_limit_id; // 1
low_limit_id; // 4 + 1 = 5,原因:ReadView生成時刻,系統(tǒng)尚未分配的下一個事務(wù)ID
creator_trx_id // 2

//事務(wù)4提交的記錄對應(yīng)的事務(wù)ID
DB_TRX_ID=4

//比較步驟
DB_TRX_ID(4)< up_limit_id(1) ? 不小于,下一步
DB_TRX_ID(4)>= low_limit_id(5) ? 不大于,下一步
m_ids.contains(DB_TRX_ID) ? 不包含,說明,事務(wù)4不在當前的活躍事務(wù)中。

//結(jié)論
故,事務(wù)4的更改,應(yīng)該看到。
所以事務(wù)2能讀到的最新數(shù)據(jù)記錄是事務(wù)4所提交的版本,而事務(wù)4提交的版本也是全局角度上最新的版本
舉例驗證:
準備測試表如下圖所示。
MySQL - 第11節(jié) - MySQL事務(wù)管理

在兩個終端各自啟動一個事務(wù),?左邊終端模擬上面的事務(wù)2,右邊終端模擬上面的事務(wù)4。在右邊終端將name(張三)改為name(李四),然后提交,在左邊終端可以看到修改后的記錄內(nèi)容,如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

8.可重復(fù)讀(RR)與讀提交(RC)的本質(zhì)區(qū)別

現(xiàn)象演示:

啟動兩個終端,將隔離級別都設(shè)置為可重復(fù)讀,并查看此時銀行用戶表中的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

在兩個終端各自啟動一個事務(wù),在左終端中的事務(wù)操作之前,先讓右終端中的事務(wù)查看一下表中的信息。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

左終端中的事務(wù)對表中的信息進行修改并提交,右終端中的事務(wù)看不到修改后的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

在右終端中使用 select ... lock in share mode 命令進行當前讀,可以看到表中的數(shù)據(jù)確實是被修改了,只是右終端中的事務(wù)看不到而已。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

但如果修改一下SQL的執(zhí)行順序,在兩個終端各自啟動一個事務(wù)后,直接讓左終端中的事務(wù)對表中的信息進行修改并提交,然后再讓右終端中的事務(wù)進行查看,這時右終端中的事務(wù)就直接看到了修改后的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

在右終端中使用 select ... lock in share mode 命令進行當前讀,可以看到剛才讀取到的確實是最新的數(shù)據(jù)。如下:

MySQL - 第11節(jié) - MySQL事務(wù)管理

說明一下:

??上面兩次實驗的唯一區(qū)別在于,右終端中的事務(wù)在左終端中的事務(wù)修改數(shù)據(jù)之前是否進行過快照讀。

??由于RR級別下要求事務(wù)內(nèi)每次讀取到的結(jié)果必須是相同的,因此事務(wù)首次進行快照讀的地方,決定了該事務(wù)后續(xù)快照讀結(jié)果的能力。

RR與RC的本質(zhì)區(qū)別:

??正是因為Read View生成時機的不同,從而造成了RC和RR級別下快照讀的結(jié)果的不同。
??在RR級別下,事務(wù)第一次進行快照讀時會創(chuàng)建一個Read View,將當前系統(tǒng)中活躍的事務(wù)記錄下來,此后再進行快照讀時就會直接使用這個Read View進行可見性判斷,因此當前事務(wù)看不到第一次快照讀之后其他事務(wù)所作的修改。
??而在RC級別下,事務(wù)每次進行快照讀時都會創(chuàng)建一個Read View,然后根據(jù)這個Read View進行可見性判斷,因此每次快照讀時都能讀取到被提交了的最新的數(shù)據(jù)。
??RR級別下快照讀只會創(chuàng)建一次Read View,所以RR級別是可重復(fù)讀的,而RC級別下每次快照讀都會創(chuàng)建新的Read View,所以RC級別是不可重復(fù)讀的。文章來源地址http://www.zghlxwxcb.cn/news/detail-512739.html

到了這里,關(guān)于MySQL - 第11節(jié) - MySQL事務(wù)管理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • MySQL事務(wù)管理

    MySQL事務(wù)管理

    什么是事務(wù)? 事務(wù)就是一組 DML 語句組成,這些語句在邏輯上存在相關(guān)性,這一組 DML 語句要么全部成功,要么全部失敗,是一個整體。MySQL 提供一種機制,保證我們達到這樣的效果。事務(wù)還規(guī)定不同的客戶端看到的數(shù)據(jù)是不相同的。 事務(wù)就是要做的或所做的事情,主要用于

    2024年01月24日
    瀏覽(22)
  • MySQL-----事務(wù)管理

    MySQL-----事務(wù)管理

    CURD不加控制的時候,會有什么問題呢? 上層看起來比較簡單的需求,可能對應(yīng)后端要做很多的工作,組合起來才是一個完整的需求解決方案. 一個整體,要么不做,要么做完(絕對成功,絕對失敗),不要出現(xiàn)中間操作這樣的概念 ---- 原子性!!! 上面就稱為一個 事務(wù) !!! 即 就是一個或者多個

    2024年02月05日
    瀏覽(16)
  • 【MySQL】MySQL索引、事務(wù)、用戶管理

    【MySQL】MySQL索引、事務(wù)、用戶管理

    20歲的男生窮困潦倒,20歲的女生風華正茂,沒有人會一直風華正茂,也沒有人會一直窮困潦倒… 1. MySQL給用戶提供存取數(shù)據(jù)的服務(wù),但數(shù)據(jù)在linux機器的磁盤外設(shè)上進行存儲,而磁盤的讀取效率是比較低的,MySQL如何進行數(shù)據(jù)存取以提高效率呢?這是一個重要的話題。 在硬件

    2024年02月14日
    瀏覽(23)
  • MySQL基礎(chǔ) — 多表查詢以及事務(wù)管理

    MySQL基礎(chǔ) — 多表查詢以及事務(wù)管理

    一對一 ? 多用于單表拆分,將一張表的基礎(chǔ)字段放在一張表中,其他詳情字段放在另一張表中,以提升操作效率 ? 在任意一方加入外鍵,關(guān)聯(lián)另外一方的主鍵,并且設(shè)置外鍵為唯一的(UNIQUE) ? 因為是一對一,所以需要設(shè)置唯一約束 一對多 ? 在多的一方建立外鍵,指向一

    2024年02月07日
    瀏覽(19)
  • 【七天入門數(shù)據(jù)庫】第七天 MySQL的事務(wù)管理

    【七天入門數(shù)據(jù)庫】第一天 MySQL的安裝部署 【七天入門數(shù)據(jù)庫】第二天 數(shù)據(jù)庫理論基礎(chǔ) 【七天入門數(shù)據(jù)庫】第三天 MySQL的庫表操作 【七天入門數(shù)據(jù)庫】第四天 數(shù)據(jù)操作語言DML 【七天入門數(shù)據(jù)庫】第五天 MySQL的備份恢復(fù) 【七天入門數(shù)據(jù)庫】第六天 MySQL的視圖與索引 【七天

    2024年02月15日
    瀏覽(90)
  • MySQL:事務(wù)、索引、用戶管理、備份、數(shù)據(jù)庫設(shè)計(三大范式)

    MySQL:事務(wù)、索引、用戶管理、備份、數(shù)據(jù)庫設(shè)計(三大范式)

    事務(wù) (transaction):要么都成功,要么都失敗。 核心 :將一組 SQL 放在一個批次中去執(zhí)行。 原則 ACID :原子性(atomicity)、一致性(consistency)、隔離性(isolation)、持久性(durability)。 原子性 :一個事務(wù)中的所有步驟 要么都 成功, 要么都 失敗,不能只成功一個步驟。 一致性 :包括

    2023年04月26日
    瀏覽(21)
  • Spring 事務(wù)管理方案和事務(wù)管理器及事務(wù)控制的API

    Spring 事務(wù)管理方案和事務(wù)管理器及事務(wù)控制的API

    目錄 一、事務(wù)管理方案 1. 修改業(yè)務(wù)層代碼 2. 測試 二、事務(wù)管理器 1. 簡介 2. 在配置文件中引入約束 3. 進行事務(wù)配置 三、事務(wù)控制的API 1.?PlatformTransactionManager接口 2.?TransactionDefinition接口 3.?TransactionStatus接口 往期專欄文章相關(guān)導(dǎo)讀? 1. Maven系列專欄文章 2. Mybatis系列專欄文

    2024年02月08日
    瀏覽(27)
  • spring事務(wù)管理詳解和實例(事務(wù)傳播機制、事務(wù)隔離級別)

    spring事務(wù)管理詳解和實例(事務(wù)傳播機制、事務(wù)隔離級別)

    目錄 1 理解spring事務(wù) 2 核心接口 2.1 事務(wù)管理器 2.1.1 JDBC事務(wù) 2.1.2 Hibernate事務(wù) 2.1.3 Java持久化API事務(wù)(JPA) 2.2 基本事務(wù)屬性的定義 2.2.1 傳播行為 2.2.2 隔離級別 2.2.3 只讀 2.2.4 事務(wù)超時 2.2.5 回滾規(guī)則 2.3 事務(wù)狀態(tài) 3?編程式事務(wù) 3.1 編程式和聲明式事務(wù)的區(qū)別 3.2 如何實現(xiàn)編程式

    2024年02月06日
    瀏覽(21)
  • 【掌握Spring事務(wù)管理】深入理解事務(wù)傳播機制的秘密

    【掌握Spring事務(wù)管理】深入理解事務(wù)傳播機制的秘密

    ?????? 點進來你就是我的人了 博主主頁: ?????? 戳一戳,歡迎大佬指點! 歡迎志同道合的朋友一起加油喔 ?????? 目錄 1.Spring 中事務(wù)的實現(xiàn)方式 1.1 Spring 編程式事務(wù) (了解) 1.2 Spring 聲明式事務(wù) ( @Transactional ) 【異常情況一】(自動回滾成功) 【異常情況二】(自動回滾失效

    2024年02月10日
    瀏覽(23)
  • Spring使用@Transactional 管理事務(wù),Java事務(wù)詳解。

    B站視頻:https://www.bilibili.com/video/BV1eV411u7cg 技術(shù)文檔:https://d9bp4nr5ye.feishu.cn/wiki/HX50wdHFyiFoLrkfEAAcTBdinvh 簡單來說事務(wù)就是一組對數(shù)據(jù)庫的操作 要么都成功,要么都失敗。 事務(wù)要保證可靠性,必須具備四個特性:ACID。 A:原子性:事務(wù)是一個原子操作單元,要么完全執(zhí)行,要么

    2024年02月11日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包