中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

標(biāo)書制作教程視頻網(wǎng)站3322免費(fèi)域名注冊(cè)

標(biāo)書制作教程視頻網(wǎng)站,3322免費(fèi)域名注冊(cè),全響應(yīng)式網(wǎng)站用什么做的,dw網(wǎng)頁設(shè)計(jì)實(shí)驗(yàn)報(bào)告1.4.事務(wù)和事務(wù)的隔離級(jí)別 1.4.1.為什么需要事務(wù) 事務(wù)是數(shù)據(jù)庫(kù)管理系統(tǒng)(DBMS)執(zhí)行過程中的一個(gè)邏輯單位(不可再進(jìn)行分割),由一個(gè)有限的數(shù)據(jù)庫(kù)操作序列構(gòu)成(多個(gè)DML語句,select語句不包含事務(wù)&…

1.4.事務(wù)和事務(wù)的隔離級(jí)別

1.4.1.為什么需要事務(wù)

事務(wù)是數(shù)據(jù)庫(kù)管理系統(tǒng)(DBMS)執(zhí)行過程中的一個(gè)邏輯單位(不可再進(jìn)行分割),由一個(gè)有限的數(shù)據(jù)庫(kù)操作序列構(gòu)成(多個(gè)DML語句,select語句不包含事務(wù)),要不全部成功,要不全部不成功。

A 給B 要?jiǎng)濆X,A 的賬戶-1000元, B 的賬戶就要+1000元,這兩個(gè)update 語句必須作為一個(gè)整體來執(zhí)行,不然A 扣錢了,B 沒有加錢這種情況就是錯(cuò)誤的。那么事務(wù)就可以保證A 、B 賬戶的變動(dòng)要么全部一起發(fā)生,要么全部一起不發(fā)生。

1.4.2.事務(wù)特性

事務(wù)應(yīng)該具有4個(gè)屬性:原子性、一致性、隔離性、持久性。這四個(gè)屬性通常稱為ACID特性。

l 原子性(atomicity)

l 一致性(consistency)

l 隔離性(isolation)

l 持久性(durability)

1.4.2.1.原子性(atomicity)

一個(gè)事務(wù)必須被視為一個(gè)不可分割的最小單元,整個(gè)事務(wù)中的所有操作要么全部提交成功,要么全部失敗,對(duì)于一個(gè)事務(wù)來說,不能只執(zhí)行其中的一部分操作。比如:

連老師借給李老師1000元:

1.連老師工資卡扣除1000元

2.李老師工資卡增加1000元

整個(gè)事務(wù)的操作要么全部成功,要么全部失敗,不能出現(xiàn)連老師工資卡扣除,但是李老師工資卡不增加的情況。如果原子性不能保證,就會(huì)很自然的出現(xiàn)一致性問題。

1.4.2.2.一致性(consistency)

一致性是指事務(wù)將數(shù)據(jù)庫(kù)從一種一致性轉(zhuǎn)換到另外一種一致性狀態(tài),在事務(wù)開始之前和事務(wù)結(jié)束之后數(shù)據(jù)庫(kù)中數(shù)據(jù)的完整性沒有被破壞。

連老師借給李老師1000元:

1.連老師工資卡扣除1000元

2.李老師工資卡增加1000元

扣除的錢(-500) 與增加的錢(500) 相加應(yīng)該為0,或者說連老師和李老師的賬戶的錢加起來,前后應(yīng)該不變。

1.4.2.3.持久性(durability)

一旦事務(wù)提交,則其所做的修改就會(huì)永久保存到數(shù)據(jù)庫(kù)中。此時(shí)即使系統(tǒng)崩潰,已經(jīng)提交的修改數(shù)據(jù)也不會(huì)丟失。

1.4.2.4.隔離性(isolation)

一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)干擾。即一個(gè)事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對(duì)并發(fā)的其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾。

如果隔離性不能保證,會(huì)導(dǎo)致什么問題?

連老師借給李老師生活費(fèi),借了兩次,每次都是1000,連老師的卡里開始有10000,李老師的卡里開始有500,從理論上,借完后,連老師的卡里有8000,李老師的卡里應(yīng)該有2500。

我們將連老師向李老師同時(shí)進(jìn)行的兩次轉(zhuǎn)賬操作分別稱為T1和T2,在現(xiàn)實(shí)世界中T1和T2是應(yīng)該沒有關(guān)系的,可以先執(zhí)行完T1,再執(zhí)行T2,或者先執(zhí)行完T2,再執(zhí)行T1,結(jié)果都是一樣的。但是很不幸,真實(shí)的數(shù)據(jù)庫(kù)中T1和T2的操作可能交替執(zhí)行的,執(zhí)行順序就有可能是:

image.png

如果按照上圖中的執(zhí)行順序來進(jìn)行兩次轉(zhuǎn)賬的話,最終我們看到,連老師的賬戶里還剩9000元錢,相當(dāng)于只扣了1000元錢,但是李老師的賬戶里卻成了2500元錢,多了10000元,這銀行豈不是要虧死了?

所以對(duì)于現(xiàn)實(shí)世界中狀態(tài)轉(zhuǎn)換對(duì)應(yīng)的某些數(shù)據(jù)庫(kù)操作來說,不僅要保證這些操作以原子性的方式執(zhí)行完成,而且要保證其它的狀態(tài)轉(zhuǎn)換不會(huì)影響到本次狀態(tài)轉(zhuǎn)換,這個(gè)規(guī)則被稱之為隔離性。

1.4.3.事務(wù)并發(fā)引發(fā)的問題

我們知道MySQL是一個(gè)客戶端/服務(wù)器架構(gòu)的軟件,對(duì)于同一個(gè)服務(wù)器來說,可以有若干個(gè)客戶端與之連接,每個(gè)客戶端與服務(wù)器連接上之后,就可以稱之為一個(gè)會(huì)話(Session)。每個(gè)客戶端都可以在自己的會(huì)話中向服務(wù)器發(fā)出請(qǐng)求語句,一個(gè)請(qǐng)求語句可能是某個(gè)事務(wù)的一部分,也就是對(duì)于服務(wù)器來說可能同時(shí)處理多個(gè)事務(wù)。

在上面我們說過事務(wù)有一個(gè)稱之為隔離性的特性,理論上在某個(gè)事務(wù)對(duì)某個(gè)數(shù)據(jù)進(jìn)行訪問時(shí),其他事務(wù)應(yīng)該進(jìn)行排隊(duì),當(dāng)該事務(wù)提交之后,其他事務(wù)才可以繼續(xù)訪問這個(gè)數(shù)據(jù),這樣的話并發(fā)事務(wù)的執(zhí)行就變成了串行化執(zhí)行。

但是對(duì)串行化執(zhí)行性能影響太大,我們既想保持事務(wù)的一定的隔離性,又想讓服務(wù)器在處理訪問同一數(shù)據(jù)的多個(gè)事務(wù)時(shí)性能盡量高些,當(dāng)我們舍棄隔離性的時(shí)候,可能會(huì)帶來什么樣的數(shù)據(jù)問題呢?

1.4.3.1.臟讀

當(dāng)一個(gè)事務(wù)讀取到了另外一個(gè)事務(wù)修改但未提交的數(shù)據(jù),被稱為臟讀。

image.png

1、在事務(wù)A執(zhí)?過程中,事務(wù)A對(duì)數(shù)據(jù)資源進(jìn)?了修改,事務(wù)B讀取了事務(wù)A修改后的數(shù)據(jù)。
2、由于某些原因,事務(wù)A并沒有完成提交,發(fā)?了RollBack操作,則事務(wù)B讀取的數(shù)據(jù)就是臟數(shù)據(jù)。
這種讀取到另?個(gè)事務(wù)未提交的數(shù)據(jù)的現(xiàn)象就是臟讀(Dirty Read)。

1.4.3.2.不可重復(fù)讀

當(dāng)事務(wù)內(nèi)相同的記錄被檢索兩次,且兩次得到的結(jié)果不同時(shí),此現(xiàn)象稱為不可重復(fù)讀。

image.png

事務(wù)B讀取了兩次數(shù)據(jù)資源,在這兩次讀取的過程中事務(wù)A修改了數(shù)據(jù),導(dǎo)致事務(wù)B在這兩次讀取出來的
數(shù)據(jù)不?致。

1.4.3.3.幻讀

在事務(wù)執(zhí)行過程中,另一個(gè)事務(wù)將新記錄添加到正在讀取的事務(wù)中時(shí),會(huì)發(fā)生幻讀。

image.png

事務(wù)B前后兩次讀取同?個(gè)范圍的數(shù)據(jù),在事務(wù)B兩次讀取的過程中事務(wù)A新增了數(shù)據(jù),導(dǎo)致事務(wù)B后?
次讀取到前?次查詢沒有看到的?。
幻讀和不可重復(fù)讀有些類似,但是幻讀重點(diǎn)強(qiáng)調(diào)了讀取到了之前讀取沒有獲取到的記錄。

1.1.4.SQL標(biāo)準(zhǔn)中的四種隔離級(jí)別

我們上邊介紹了幾種并發(fā)事務(wù)執(zhí)行過程中可能遇到的一些問題,這些問題也有輕重緩急之分,我們給這些問題按照嚴(yán)重性來排一下序:

臟讀 > 不可重復(fù)讀 > 幻讀

我們上邊所說的舍棄一部分隔離性來換取一部分性能在這里就體現(xiàn)在:設(shè)立一些隔離級(jí)別,隔離級(jí)別越低,越嚴(yán)重的問題就越可能發(fā)生。有一幫人(并不是設(shè)計(jì)MySQL的大叔們)制定了一個(gè)所謂的SQL標(biāo)準(zhǔn),在標(biāo)準(zhǔn)中設(shè)立了4個(gè)隔離級(jí)別:

READ UNCOMMITTED:未提交讀。

READ COMMITTED:已提交讀。

REPEATABLE READ:可重復(fù)讀。

SERIALIZABLE:可串行化。

SQL標(biāo)準(zhǔn)中規(guī)定,針對(duì)不同的隔離級(jí)別,并發(fā)事務(wù)可以發(fā)生不同嚴(yán)重程度的問題,具體情況如下:

也就是說:

READ UNCOMMITTED隔離級(jí)別下,可能發(fā)生臟讀、不可重復(fù)讀和幻讀問題。

READ COMMITTED隔離級(jí)別下,可能發(fā)生不可重復(fù)讀和幻讀問題,但是不可以發(fā)生臟讀問題。

REPEATABLE READ隔離級(jí)別下,可能發(fā)生幻讀問題,但是不可以發(fā)生臟讀和不可重復(fù)讀的問題。

SERIALIZABLE隔離級(jí)別下,各種問題都不可以發(fā)生。

image.png

1.4.5.MySQL中的隔離級(jí)別

不同的數(shù)據(jù)庫(kù)廠商對(duì)SQL標(biāo)準(zhǔn)中規(guī)定的四種隔離級(jí)別支持不一樣,比方說Oracle就只支持READ COMMITTED和SERIALIZABLE隔離級(jí)別。本書中所討論的MySQL雖然支持4種隔離級(jí)別,但與SQL標(biāo)準(zhǔn)中所規(guī)定的各級(jí)隔離級(jí)別允許發(fā)生的問題卻有些出入,MySQL在REPEATABLE READ隔離級(jí)別下,是可以禁止幻讀問題的發(fā)生的。

image.png

MySQL的默認(rèn)隔離級(jí)別為REPEATABLE READ,我們可以手動(dòng)修改事務(wù)的隔離級(jí)別。

1.4.5.1.如何設(shè)置事務(wù)的隔離級(jí)別

我們可以通過下邊的語句修改事務(wù)的隔離級(jí)別:

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;

其中的level可選值有4個(gè):

level: {REPEATABLE READ| READ COMMITTED| READ UNCOMMITTED| SERIALIZABLE
}

設(shè)置事務(wù)的隔離級(jí)別的語句中,在SET關(guān)鍵字后可以放置GLOBAL關(guān)鍵字、SESSION關(guān)鍵字或者什么都不放,這樣會(huì)對(duì)不同范圍的事務(wù)產(chǎn)生不同的影響,具體如下:

使用GLOBAL關(guān)鍵字(在全局范圍影響):

比方說這樣:

SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;

則: 只對(duì)執(zhí)行完該語句之后產(chǎn)生的會(huì)話起作用。當(dāng)前已經(jīng)存在的會(huì)話無效。

使用SESSION關(guān)鍵字(在會(huì)話范圍影響):

比方說這樣:

SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

則:對(duì)當(dāng)前會(huì)話的所有后續(xù)的事務(wù)有效

該語句可以在已經(jīng)開啟的事務(wù)中間執(zhí)行,但不會(huì)影響當(dāng)前正在執(zhí)行的事務(wù)。

如果在事務(wù)之間執(zhí)行,則對(duì)后續(xù)的事務(wù)有效。

上述兩個(gè)關(guān)鍵字都不用(只對(duì)執(zhí)行語句后的下一個(gè)事務(wù)產(chǎn)生影響):

比方說這樣:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

則:只對(duì)當(dāng)前會(huì)話中下一個(gè)即將開啟的事務(wù)有效。下一個(gè)事務(wù)執(zhí)行完后,后續(xù)事務(wù)將恢復(fù)到之前的隔離級(jí)別。該語句不能在已經(jīng)開啟的事務(wù)中間執(zhí)行,會(huì)報(bào)錯(cuò)的。

如果我們?cè)诜?wù)器啟動(dòng)時(shí)想改變事務(wù)的默認(rèn)隔離級(jí)別,可以修改啟動(dòng)參數(shù)transaction-isolation的值,比方說我們?cè)趩?dòng)服務(wù)器時(shí)指定了–transaction-isolation=SERIALIZABLE,那么事務(wù)的默認(rèn)隔離級(jí)別就從原來的REPEATABLE READ變成了SERIALIZABLE。

想要查看當(dāng)前會(huì)話默認(rèn)的隔離級(jí)別可以通過查看系統(tǒng)變量transaction_isolation的值來確定:

SHOW VARIABLES LIKE 'transaction_isolation';

image.png

或者使用更簡(jiǎn)便的寫法:

SELECT @@transaction_isolation;

image.png

注意:transaction_isolation是在MySQL 5.7.20的版本中引入來替換tx_isolation的,如果你使用的是之前版本的MySQL,請(qǐng)將上述用到系統(tǒng)變量transaction_isolation的地方替換為tx_isolation。

1.4.6.MySQL事務(wù)

1.4.6.1.事務(wù)基本語法

事務(wù)開始

1、begin

2、START TRANSACTION(推薦)

3、begin work

事務(wù)回滾

rollback

事務(wù)提交

commit

使用事務(wù)插入兩行數(shù)據(jù),commit后數(shù)據(jù)還在

image.png

使用事務(wù)插入兩行數(shù)據(jù),rollback后數(shù)據(jù)沒有了

image.png

1.4.6.2.保存點(diǎn)

如果你開啟了一個(gè)事務(wù),執(zhí)行了很多語句,忽然發(fā)現(xiàn)某條語句有點(diǎn)問題,你只好使用ROLLBACK語句來讓數(shù)據(jù)庫(kù)狀態(tài)恢復(fù)到事務(wù)執(zhí)行之前的樣子,然后一切從頭再來,但是可能根據(jù)業(yè)務(wù)和數(shù)據(jù)的變化,不需要全部回滾。所以MySQL里提出了一個(gè)保存點(diǎn)(英文:savepoint)的概念,就是在事務(wù)對(duì)應(yīng)的數(shù)據(jù)庫(kù)語句中打幾個(gè)點(diǎn),我們?cè)谡{(diào)用ROLLBACK語句時(shí)可以指定會(huì)滾到哪個(gè)點(diǎn),而不是回到最初的原點(diǎn)。定義保存點(diǎn)的語法如下:

SAVEPOINT 保存點(diǎn)名稱;

當(dāng)我們想回滾到某個(gè)保存點(diǎn)時(shí),可以使用下邊這個(gè)語句(下邊語句中的單詞WORK和SAVEPOINT是可有可無的):

ROLLBACK TO [SAVEPOINT] 保存點(diǎn)名稱;

不過如果ROLLBACK語句后邊不跟隨保存點(diǎn)名稱的話,會(huì)直接回滾到事務(wù)執(zhí)行之前的狀態(tài)。

如果我們想刪除某個(gè)保存點(diǎn),可以使用這個(gè)語句:

RELEASE SAVEPOINT 保存點(diǎn)名稱;

image.png

image.png

image.png

1.4.6.3.隱式提交

當(dāng)我們使用START TRANSACTION或者BEGIN語句開啟了一個(gè)事務(wù),或者把系統(tǒng)變量autocommit的值設(shè)置為OFF時(shí),事務(wù)就不會(huì)進(jìn)行自動(dòng)提交,但是如果我們輸入了某些語句之后就會(huì)悄悄的提交掉,就像我們輸入了COMMIT語句了一樣,這種因?yàn)槟承┨厥獾恼Z句而導(dǎo)致事務(wù)提交的情況稱為隱式提交,這些會(huì)導(dǎo)致事務(wù)隱式提交的語句包括:

1.4.6.3.1.執(zhí)行DDL

定義或修改數(shù)據(jù)庫(kù)對(duì)象的數(shù)據(jù)定義語言(Datadefinition language,縮寫為:DDL)。

所謂的數(shù)據(jù)庫(kù)對(duì)象,指的就是數(shù)據(jù)庫(kù)、表、視圖、存儲(chǔ)過程等等這些東西。當(dāng)我們使用CREATE、ALTER、DROP等語句去修改這些所謂的數(shù)據(jù)庫(kù)對(duì)象時(shí),就會(huì)隱式的提交前邊語句所屬于的事務(wù),就像這樣:

BEGIN;SELECT ... # 事務(wù)中的一條語句UPDATE ... # 事務(wù)中的一條語句... # 事務(wù)中的其它語句CREATE TABLE ...

image.png

image.png

此語句會(huì)隱式的提交前邊語句所屬于的事務(wù)

1.4.6.3.2.隱式使用或修改mysql數(shù)據(jù)庫(kù)中的表

當(dāng)我們使用ALTER USER、CREATE USER、DROP USER、GRANT、RENAME USER、REVOKE、SET PASSWORD等語句時(shí)也會(huì)隱式的提交前邊語句所屬于的事務(wù)。

1.4.6.3.3.事務(wù)控制或關(guān)于鎖定的語句

當(dāng)我們?cè)谝粋€(gè)會(huì)話里,一個(gè)事務(wù)還沒提交或者回滾時(shí)就又使用START TRANSACTION或者BEGIN語句開啟了另一個(gè)事務(wù)時(shí),會(huì)隱式的提交上一個(gè)事務(wù),比如這樣:

BEGIN;SELECT ... # 事務(wù)中的一條語句UPDATE ... # 事務(wù)中的一條語句... # 事務(wù)中的其它語句BEGIN; # 此語句會(huì)隱式的提交前邊語句所屬于的事務(wù)

image.png

image.png

或者當(dāng)前的autocommit系統(tǒng)變量的值為OFF,我們手動(dòng)把它調(diào)為ON時(shí),也會(huì)隱式的提交前邊語句所屬的事務(wù)。

或者使用LOCK TABLES、UNLOCK TABLES等關(guān)于鎖定的語句也會(huì)隱式的提交前邊語句所屬的事務(wù)。

1.4.6.3.4.加載數(shù)據(jù)的語句

比如我們使用LOAD DATA語句來批量往數(shù)據(jù)庫(kù)中導(dǎo)入數(shù)據(jù)時(shí),也會(huì)隱式的提交前邊語句所屬的事務(wù)。

1.4.6.3.5.關(guān)于MySQL復(fù)制的一些語句

使用START SLAVE、STOP SLAVE、RESET SLAVE、CHANGE MASTER TO等語句時(shí)也會(huì)隱式的提交前邊語句所屬的事務(wù)。

1.4.6.3.6.其它的一些語句

使用ANALYZE TABLE、CACHE INDEX、CHECK TABLE、FLUSH、 LOAD INDEX INTO CACHE、OPTIMIZE TABLE、REPAIR TABLE、RESET等語句也會(huì)隱式的提交前邊語句所屬的事務(wù)。

1.5. MVCC

全稱Multi-Version Concurrency Control,即多版本并發(fā)控制,主要是為了提高數(shù)據(jù)庫(kù)的并發(fā)性能。
同一行數(shù)據(jù)平時(shí)發(fā)生讀寫請(qǐng)求時(shí),會(huì)上鎖阻塞住。但MVCC用更好的方式去處理讀—寫請(qǐng)求,做到在發(fā)生讀—寫請(qǐng)求沖突時(shí)不用加鎖。
這個(gè)讀是指的快照讀,而不是當(dāng)前讀,當(dāng)前讀是一種加鎖操作,是悲觀鎖。
那它到底是怎么做到讀—寫不用加鎖的,快照讀和當(dāng)前讀是指什么?我們后面都會(huì)學(xué)到。

1.5.1.MVCC原理

1.5.1.1.復(fù)習(xí)事務(wù)隔離級(jí)別image.png

MySQL在REPEATABLE READ隔離級(jí)別下,是可以很大程度避免幻讀問題的發(fā)生的(好像解決了,但是又沒完全解決),MySQL是怎么做到的?

1.5.1.2.版本鏈

必須要知道的概念(每個(gè)版本鏈針對(duì)的一條數(shù)據(jù)):

我們知道,對(duì)于使用InnoDB存儲(chǔ)引擎的表來說,它的聚簇索引記錄中都包含兩個(gè)必要的隱藏列(row_id并不是必要的,我們創(chuàng)建的表中有主鍵或者非NULL的UNIQUE鍵時(shí)都不會(huì)包含row_id列):
trx_id:每次一個(gè)事務(wù)對(duì)某條聚簇索引記錄進(jìn)行改動(dòng)時(shí),都會(huì)把該事務(wù)的事務(wù)id賦值給trx_id隱藏列。
roll_pointer:每次對(duì)某條聚簇索引記錄進(jìn)行改動(dòng)時(shí),都會(huì)把舊的版本寫入到undo日志中,然后這個(gè)隱藏列就相當(dāng)于一個(gè)指針,可以通過它來找到該記錄修改前的信息。

(補(bǔ)充點(diǎn):undo日志:為了實(shí)現(xiàn)事務(wù)的原子性,InnoDB存儲(chǔ)引擎在實(shí)際進(jìn)行增、刪、改一條記錄時(shí),都需要先把對(duì)應(yīng)的undo日志記下來。一般每對(duì)一條記錄做一次改動(dòng),就對(duì)應(yīng)著一條undo日志,但在某些更新記錄的操作中,也可能會(huì)對(duì)應(yīng)著2條undo日志。一個(gè)事務(wù)在執(zhí)行過程中可能新增、刪除、更新若干條記錄,也就是說需要記錄很多條對(duì)應(yīng)的undo日志,這些undo日志會(huì)被從0開始編號(hào),也就是說根據(jù)生成的順序分別被稱為第0號(hào)undo日志、第1號(hào)undo日志、…、第n號(hào)undo日志等,這個(gè)編號(hào)也被稱之為undo no。)

為了說明這個(gè)問題,我們創(chuàng)建一個(gè)演示表

CREATE TABLE teacher (
number INT,
name VARCHAR(100),
domain varchar(100),
PRIMARY KEY (number)
) Engine=InnoDB CHARSET=utf8;

然后向這個(gè)表里插入一條數(shù)據(jù):

INSERT INTO teacher VALUES(1, '李瑾', 'JVM系列');

現(xiàn)在表里的數(shù)據(jù)就是這樣的:

image.png

假設(shè)插入該記錄的事務(wù)id為60,那么此刻該條記錄的示意圖如下所示:

image.png

假設(shè)之后兩個(gè)事務(wù)id分別為80、120的事務(wù)對(duì)這條記錄進(jìn)行UPDATE操作,操作流程如下:
image.png

每次對(duì)記錄進(jìn)行改動(dòng),都會(huì)記錄一條undo日志,每條undo日志也都有一個(gè)roll_pointer屬性(INSERT操作對(duì)應(yīng)的undo日志沒有該屬性,因?yàn)樵撚涗洸]有更早的版本),可以將這些undo日志都連起來,串成一個(gè)鏈表,所以現(xiàn)在的情況就像下圖一樣:

image.png

對(duì)該記錄每次更新后,都會(huì)將舊值放到一條undo日志中,就算是該記錄的一個(gè)舊版本,隨著更新次數(shù)的增多,所有的版本都會(huì)被roll_pointer屬性連接成一個(gè)鏈表,我們把這個(gè)鏈表稱之為版本鏈,版本鏈的頭節(jié)點(diǎn)就是當(dāng)前記錄最新的值。另外,每個(gè)版本中還包含生成該版本時(shí)對(duì)應(yīng)的事務(wù)id。于是可以利用這個(gè)記錄的版本鏈來控制并發(fā)事務(wù)訪問相同記錄的行為,那么這種機(jī)制就被稱之為多版本并發(fā)控制(Mulit-Version Concurrency Control MVCC)。

1.5.1.3.ReadView

必須要知道的概念(作用于SQL查詢語句)

對(duì)于使用READ UNCOMMITTED隔離級(jí)別的事務(wù)來說,由于可以讀到未提交事務(wù)修改過的記錄,所以直接讀取記錄的最新版本就好了(所以就會(huì)出現(xiàn)臟讀、不可重復(fù)讀、幻讀)。

image.png
對(duì)于使用SERIALIZABLE隔離級(jí)別的事務(wù)來說,InnoDB使用加鎖的方式來訪問記錄(也就是所有的事務(wù)都是串行的,當(dāng)然不會(huì)出現(xiàn)臟讀、不可重復(fù)讀、幻讀)。

image.png
對(duì)于使用READ COMMITTED和REPEATABLE READ隔離級(jí)別的事務(wù)來說,都必須保證讀到已經(jīng)提交了的事務(wù)修改過的記錄,也就是說假如另一個(gè)事務(wù)已經(jīng)修改了記錄但是尚未提交,是不能直接讀取最新版本的記錄的,核心問題就是:READ COMMITTED和REPEATABLE READ隔離級(jí)別在不可重復(fù)讀和幻讀上的區(qū)別是從哪里來的,其實(shí)結(jié)合前面的知識(shí),這兩種隔離級(jí)別關(guān)鍵是需要判斷一下版本鏈中的哪個(gè)版本是當(dāng)前事務(wù)可見的。
為此,InnoDB提出了一個(gè)ReadView的概念(作用于SQL查詢語句),

這個(gè)ReadView中主要包含4個(gè)比較重要的內(nèi)容:
**m_ids:**表示在生成ReadView時(shí)當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)的事務(wù)id列表。
**min_trx_id:**表示在生成ReadView時(shí)當(dāng)前系統(tǒng)中活躍的讀寫事務(wù)中最小的事務(wù)id,也就是m_ids中的最小值。
**max_trx_id:**表示生成ReadView時(shí)系統(tǒng)中應(yīng)該分配給下一個(gè)事務(wù)的id值。注意max_trx_id并不是m_ids中的最大值,事務(wù)id是遞增分配的。比方說現(xiàn)在有id為1,2,3這三個(gè)事務(wù),之后id為3的事務(wù)提交了。那么一個(gè)新的讀事務(wù)在生成ReadView時(shí),m_ids就包括1和2,min_trx_id的值就是1,max_trx_id的值就是4。
**creator_trx_id:**表示生成該ReadView的事務(wù)的事務(wù)id。

1.5.1.4.READ COMMITTED

臟讀問題的解決

READ COMMITTED隔離級(jí)別的事務(wù)在每次查詢開始時(shí)都會(huì)生成一個(gè)獨(dú)立的ReadView。

在MySQL中,READ COMMITTED和REPEATABLE READ隔離級(jí)別的的一個(gè)非常大的區(qū)別就是它們生成ReadView的時(shí)機(jī)不同。
我們還是以表teacher 為例,假設(shè)現(xiàn)在表teacher 中只有一條由事務(wù)id為60的事務(wù)插入的一條記錄,接下來看一下READ COMMITTED和REPEATABLE READ所謂的生成ReadView的時(shí)機(jī)不同到底不同在哪里。
READ COMMITTED —— 每次讀取數(shù)據(jù)前都生成一個(gè)ReadView
比方說現(xiàn)在系統(tǒng)里有兩個(gè)事務(wù)id分別為80、120的事務(wù)在執(zhí)行:Transaction 80

UPDATE teacher  SET name = '馬' WHERE number = 1;
UPDATE teacher  SET name = '連' WHERE number = 1;
...

此刻,表teacher 中number為1的記錄得到的版本鏈表如下所示:

image.png

假設(shè)現(xiàn)在有一個(gè)使用READ COMMITTED隔離級(jí)別的事務(wù)開始執(zhí)行:

image.png

使用READ COMMITTED隔離級(jí)別的事務(wù)BEGIN;
SELECE1:Transaction 80、120未提交SELECT * FROM teacher WHERE number = 1; # 得到的列name的值為'李瑾'

第1次select的時(shí)間點(diǎn) 如下圖:

image.png

這個(gè)SELECE1的執(zhí)行過程如下:
在執(zhí)行SELECT語句時(shí)會(huì)先生成一個(gè)ReadView:

ReadView的m_ids列表的內(nèi)容就是[80, 120],min_trx_id為80,max_trx_id為121,creator_trx_id為0。

然后從版本鏈中挑選可見的記錄,從圖中可以看出,最新版本的列name的內(nèi)容是’’,該版本的trx_id值為80,在m_ids列表內(nèi),所以不符合可見性要求(trx_id屬性值在ReadView的min_trx_id和max_trx_id之間說明創(chuàng)建ReadView時(shí)生成該版本的事務(wù)還是活躍的,該版本不可以被訪問;如果不在,說明創(chuàng)建ReadView時(shí)生成該版本的事務(wù)已經(jīng)被提交,該版本可以被訪問),根據(jù)roll_pointer跳到下一個(gè)版本。
下一個(gè)版本的列name的內(nèi)容是’’,該版本的trx_id值也為80,也在m_ids列表內(nèi),所以也不符合要求,繼續(xù)跳到下一個(gè)版本。
下一個(gè)版本的列name的內(nèi)容是’李瑾’,該版本的trx_id值為60,小于ReadView中的min_trx_id值,所以這個(gè)版本是符合要求的,最后返回給用戶的版本就是這條列name為’李瑾’的記錄。

所以有了這種機(jī)制,就不會(huì)發(fā)生臟讀問題!因?yàn)闀?huì)去判斷活躍版本,必須是不在活躍版本的才能用,不可能讀到?jīng)]有 commit的記錄。

image.png

不可重復(fù)讀問題

然后,我們把事務(wù)id為80的事務(wù)提交一下,然后再到事務(wù)id為120的事務(wù)中更新一下表teacher 中number為1的記錄:

image.png

Transaction120BEGIN;更新了一些別的表的記錄UPDATE teacher  SET name = '嚴(yán)' WHERE number = 1;
UPDATE teacher  SET name = '晁' WHERE number = 1;

此刻,表teacher 中number為1的記錄的版本鏈就長(zhǎng)這樣:

image.png

然后再到剛才使用READ COMMITTED隔離級(jí)別的事務(wù)中繼續(xù)查找這個(gè)number為1的記錄,如下:

使用READ COMMITTED隔離級(jí)別的事務(wù)

BEGIN;SELECE1:Transaction 80、120均未提交SELECT * FROM teacher WHERE number = 1; # 得到的列name的值為'李瑾'SELECE2:Transaction 80提交,Transaction 120未提交SELECT * FROM teacher WHERE number = 1; # 得到的列name的值為'連'

第2次select的時(shí)間點(diǎn) 如下圖:

image.png

這個(gè)SELECE2的執(zhí)行過程如下:

SELECT * FROM teacher WHERE number = 1;

在執(zhí)行SELECT語句時(shí)會(huì)又會(huì)單獨(dú)生成一個(gè)ReadView,該ReadView信息如下:

m_ids列表的內(nèi)容就是[120](事務(wù)id為80的那個(gè)事務(wù)已經(jīng)提交了,所以再次生成快照時(shí)就沒有它了),min_trx_id為120,max_trx_id為121,creator_trx_id為0。
然后從版本鏈中挑選可見的記錄,從圖中可以看出,最新版本的列name的內(nèi)容是’’,該版本的trx_id值為120,在m_ids列表內(nèi),所以不符合可見性要求,根據(jù)roll_pointer跳到下一個(gè)版本。
下一個(gè)版本的列name的內(nèi)容是’嚴(yán)’,該版本的trx_id值為120,也在m_ids列表內(nèi),所以也不符合要求,繼續(xù)跳到下一個(gè)版本。
下一個(gè)版本的列name的內(nèi)容是’’,該版本的trx_id值為80,小于ReadView中的min_trx_id值120,所以這個(gè)版本是符合要求的,最后返回給用戶的版本就是這條列name為’’的記錄。

以此類推,如果之后事務(wù)id為120的記錄也提交了,再次在使用READ COMMITTED隔離級(jí)別的事務(wù)中查詢表teacher 中number值為1的記錄時(shí),得到的結(jié)果就是’’了,具體流程我們就不分析了。

image.png

但會(huì)出現(xiàn)不可重復(fù)讀問題。

明顯上面一個(gè)事務(wù)中兩次

image.png

1.5.1.5.REPEATABLE READ

REPEATABLE READ解決不可重復(fù)讀問題

REPEATABLE READ —— 在第一次讀取數(shù)據(jù)時(shí)生成一個(gè)ReadView

對(duì)于使用REPEATABLE READ隔離級(jí)別的事務(wù)來說,只會(huì)在第一次執(zhí)行查詢語句時(shí)生成一個(gè)ReadView,之后的查詢就不會(huì)重復(fù)生成了。我們還是用例子看一下是什么效果。

比方說現(xiàn)在系統(tǒng)里有兩個(gè)事務(wù)id分別為80、120的事務(wù)在執(zhí)行:Transaction 80

UPDATE teacher  SET name = '馬' WHERE number = 1;
UPDATE teacher  SET name = '連' WHERE number = 1;
...

此刻,表teacher 中number為1的記錄得到的版本鏈表如下所示:

image.png

假設(shè)現(xiàn)在有一個(gè)使用REPEATABLE READ隔離級(jí)別的事務(wù)開始執(zhí)行:

image.png

使用READ COMMITTED隔離級(jí)別的事務(wù)BEGIN;
SELECE1:Transaction 80、120未提交SELECT * FROM teacher WHERE number = 1; # 得到的列name的值為'李瑾'

這個(gè)SELECE1的執(zhí)行過程如下:
在執(zhí)行SELECT語句時(shí)會(huì)先生成一個(gè)ReadView:

ReadView的m_ids列表的內(nèi)容就是[80, 120],min_trx_id為80,max_trx_id為121,creator_trx_id為0。

然后從版本鏈中挑選可見的記錄,從圖中可以看出,最新版本的列name的內(nèi)容是’’,該版本的trx_id值為80,在m_ids列表內(nèi),所以不符合可見性要求(trx_id屬性值在ReadView的min_trx_id和max_trx_id之間說明創(chuàng)建ReadView時(shí)生成該版本的事務(wù)還是活躍的,該版本不可以被訪問;如果不在,說明創(chuàng)建ReadView時(shí)生成該版本的事務(wù)已經(jīng)被提交,該版本可以被訪問),根據(jù)roll_pointer跳到下一個(gè)版本。
下一個(gè)版本的列name的內(nèi)容是’’,該版本的trx_id值也為80,也在m_ids列表內(nèi),所以也不符合要求,繼續(xù)跳到下一個(gè)版本。
下一個(gè)版本的列name的內(nèi)容是’李瑾’,該版本的trx_id值為60,小于ReadView中的min_trx_id值,所以這個(gè)版本是符合要求的,最后返回給用戶的版本就是這條列name為’李瑾’的記錄。
之后,我們把事務(wù)id為80的事務(wù)提交一下,然后再到事務(wù)id為120的事務(wù)中更新一下表teacher 中number為1的記錄:

image.png

Transaction120BEGIN;更新了一些別的表的記錄UPDATE teacher  SET name = '嚴(yán)' WHERE number = 1;
UPDATE teacher  SET name = '晁' WHERE number = 1;

此刻,表teacher 中number為1的記錄的版本鏈就長(zhǎng)這樣:

image.png

然后再到剛才使用REPEATABLE READ隔離級(jí)別的事務(wù)中繼續(xù)查找這個(gè)number為1的記錄,如下:

使用READ COMMITTED隔離級(jí)別的事務(wù)

BEGIN;SELECE1:Transaction 80、120均未提交SELECT * FROM teacher WHERE number = 1; # 得到的列name的值為'李瑾'SELECE2:Transaction 80提交,Transaction 120未提交SELECT * FROM teacher WHERE number = 1; # 得到的列name的值為'李瑾'

這個(gè)SELECE2的執(zhí)行過程如下:

因?yàn)楫?dāng)前事務(wù)的隔離級(jí)別為REPEATABLE READ,而之前在執(zhí)行SELECE1時(shí)已經(jīng)生成過ReadView了,所以此時(shí)直接復(fù)用之前的ReadView,之前的ReadView的m_ids列表的內(nèi)容就是[80, 120],min_trx_id為80,max_trx_id為121,creator_trx_id為0。

根據(jù)前面的分析,返回的值還是’李瑾’。

也就是說兩次SELECT查詢得到的結(jié)果是重復(fù)的,記錄的列name值都是’李瑾’,這就是可重復(fù)讀的含義。

image.png

總結(jié)一下就是:

ReadView中的比較規(guī)則(前兩條)

1、如果被訪問版本的trx_id屬性值與ReadView中的creator_trx_id值相同,意味著當(dāng)前事務(wù)在訪問它自己修改過的記錄,所以該版本可以被當(dāng)前事務(wù)訪問。

2、如果被訪問版本的trx_id屬性值小于ReadView中的min_trx_id值,表明生成該版本的事務(wù)在當(dāng)前事務(wù)生成ReadView前已經(jīng)提交,所以該版本可以被當(dāng)前事務(wù)訪問。

1.5.1.6.MVCC下的幻讀解決和幻讀現(xiàn)象

前面我們已經(jīng)知道了,REPEATABLE READ隔離級(jí)別下MVCC可以解決不可重復(fù)讀問題,那么幻讀呢?MVCC是怎么解決的?幻讀是一個(gè)事務(wù)按照某個(gè)相同條件多次讀取記錄時(shí),后讀取時(shí)讀到了之前沒有讀到的記錄,而這個(gè)記錄來自另一個(gè)事務(wù)添加的新記錄。
我們可以想想,在REPEATABLE READ隔離級(jí)別下的事務(wù)T1先根據(jù)某個(gè)搜索條件讀取到多條記錄,然后事務(wù)T2插入一條符合相應(yīng)搜索條件的記錄并提交,然后事務(wù)T1再根據(jù)相同搜索條件執(zhí)行查詢。結(jié)果會(huì)是什么?按照ReadView中的比較規(guī)則(后兩條):
3、如果被訪問版本的trx_id屬性值大于或等于ReadView中的max_trx_id值,表明生成該版本的事務(wù)在當(dāng)前事務(wù)生成ReadView后才開啟,所以該版本不可以被當(dāng)前事務(wù)訪問。
4、如果被訪問版本的trx_id屬性值在ReadView的min_trx_id和max_trx_id之間(min_trx_id < trx_id < max_trx_id),那就需要判斷一下trx_id屬性值是不是在m_ids列表中,如果在,說明創(chuàng)建ReadView時(shí)生成該版本的事務(wù)還是活躍的,該版本不可以被訪問;如果不在,說明創(chuàng)建ReadView時(shí)生成該版本的事務(wù)已經(jīng)被提交,該版本可以被訪問。

不管事務(wù)T2比事務(wù)T1是否先開啟,事務(wù)T1都是看不到T2的提交的。請(qǐng)自行按照上面介紹的版本鏈、ReadView以及判斷可見性的規(guī)則來分析一下。
但是,在REPEATABLE READ隔離級(jí)別下InnoDB中的MVCC 可以很大程度地避免幻讀現(xiàn)象,而不是完全禁止幻讀。怎么回事呢?我們來看下面的情況:

image.png

我們首先在事務(wù)T1中:

select * from teacher where number = 30;

很明顯,這個(gè)時(shí)候是找不到number = 30的記錄的。
我們?cè)谑聞?wù)T2中,執(zhí)行:

insert into teacher values(30,'豹','數(shù)據(jù)湖');

image.png

通過執(zhí)行insert into teacher values(30,‘豹’,‘?dāng)?shù)據(jù)湖’);,我們往表中插入了一條number = 30的記錄。
此時(shí)回到事務(wù)T1,執(zhí)行:

update teacher set domain='RocketMQ' where number=30;
select * from teacher where number = 30;

image.png

嗯,怎么回事?事務(wù)T1很明顯出現(xiàn)了幻讀現(xiàn)象。
在REPEATABLE READ隔離級(jí)別下,T1第一次執(zhí)行普通的SELECT 語句時(shí)生成了一個(gè)ReadView(但是版本鏈沒有),之后T2向teacher 表中新插入一條記錄并提交,然后T1也進(jìn)行了一個(gè)update語句。
ReadView并不能阻止T1執(zhí)行UPDATE 或者DELETE 語句來改動(dòng)這個(gè)新插入的記錄,但是這樣一來,這條新記錄的trx_id隱藏列的值就變成了T1的事務(wù)id。

image.png

之后T1再使用普通的SELECT 語句去查詢這條記錄時(shí)就可以看到這條記錄了,也就可以把這條記錄返回給客戶端。因?yàn)檫@個(gè)特殊現(xiàn)象的存在,我們也可以認(rèn)為MVCC 并不能完全禁止幻讀(就是第一次讀如果是空的情況,且在自己事務(wù)中進(jìn)行了該條數(shù)據(jù)的修改)。

1.5.1.7.MVCC小結(jié)

從上邊的描述中我們可以看出來,所謂的MVCC(Multi-Version Concurrency Control ,多版本并發(fā)控制)指的就是在使用READ COMMITTD、REPEATABLE READ這兩種隔離級(jí)別的事務(wù)在執(zhí)行普通的SELECT操作時(shí)訪問記錄的版本鏈的過程,這樣子可以使不同事務(wù)的讀-寫、寫-讀操作并發(fā)執(zhí)行,從而提升系統(tǒng)性能。

READ COMMITTD、REPEATABLE READ這兩個(gè)隔離級(jí)別的一個(gè)很大不同就是:生成ReadView的時(shí)機(jī)不同,READ COMMITTD在每一次進(jìn)行普通SELECT操作前都會(huì)生成一個(gè)ReadView,而REPEATABLE READ只在第一次進(jìn)行普通SELECT操作前生成一個(gè)ReadView,之后的查詢操作都重復(fù)使用這個(gè)ReadView就好了,從而基本上可以避免幻讀現(xiàn)象(就是第一次讀如果ReadView是空的情況中的某些情況則避免不了)。

另外,所謂的MVCC只是在我們進(jìn)行普通的SEELCT查詢時(shí)才生效,截止到目前我們所見的所有SELECT語句都算是普通的查詢,至于什么是個(gè)不普通的查詢,后面馬上就會(huì)講到(鎖定讀)。

http://www.risenshineclean.com/news/38630.html

相關(guān)文章:

  • 網(wǎng)站網(wǎng)址怎么找電商運(yùn)營(yíng)培訓(xùn)班多少錢
  • 彩票代購(gòu)網(wǎng)站建設(shè)電腦優(yōu)化軟件哪個(gè)好用
  • 以個(gè)人名義可以做網(wǎng)站嗎蘋果自研搜索引擎或?yàn)樘娲雀?/a>
  • 如果想看網(wǎng)站的收費(fèi)電影應(yīng)該怎么做惠州關(guān)鍵詞排名提升
  • 怎么把自己做的網(wǎng)站掛到外網(wǎng)上sem代運(yùn)營(yíng)
  • 2018年做返利網(wǎng)站網(wǎng)站功能優(yōu)化
  • 仿牌外貿(mào)網(wǎng)站百度搜索高級(jí)搜索
  • 做網(wǎng)站是干嘛廈門人才網(wǎng)最新招聘信息
  • 福州市住房和城鄉(xiāng)建設(shè)部網(wǎng)站seo是什么意思怎么解決
  • 建立英文網(wǎng)站搜索引擎優(yōu)化到底是優(yōu)化什么
  • 網(wǎng)站建設(shè)技術(shù)人員要會(huì)什么企業(yè)查詢官網(wǎng)入口
  • 疫情最新資訊seo費(fèi)用
  • 什么是php動(dòng)態(tài)網(wǎng)站開發(fā)知名的網(wǎng)絡(luò)推廣
  • 金藏源電商網(wǎng)站建設(shè)怎樣免費(fèi)制作網(wǎng)頁
  • 時(shí)尚網(wǎng)站哪里有整站優(yōu)化
  • 上海企業(yè)網(wǎng)站建設(shè)公谷歌paypal官網(wǎng)注冊(cè)入口
  • 可以做商城網(wǎng)站的公司嗎中國(guó)國(guó)家培訓(xùn)網(wǎng)官網(wǎng)入口
  • 網(wǎng)站背景css培訓(xùn)心得簡(jiǎn)短
  • 科技網(wǎng)站設(shè)計(jì)公司西安網(wǎng)站建設(shè)哪家好
  • 福建省建設(shè)執(zhí)業(yè)繼續(xù)教育網(wǎng)站百度關(guān)鍵詞點(diǎn)擊器
  • 做網(wǎng)站收藏的網(wǎng)頁搜索熱門關(guān)鍵詞
  • 做棋牌網(wǎng)站賺錢嗎域名查詢網(wǎng)入口
  • wordpress 外貿(mào)站主題seo管理是什么
  • 國(guó)家工業(yè)和信息化部網(wǎng)站備案系統(tǒng)中國(guó)十大電商培訓(xùn)機(jī)構(gòu)
  • 北京網(wǎng)站建設(shè) 一流seo外包服務(wù)
  • 宿松網(wǎng)站建設(shè)青島關(guān)鍵詞排名哪家好
  • 北京上海網(wǎng)站建設(shè)無經(jīng)驗(yàn)?zāi)茏鰏em專員
  • dremrever怎么做網(wǎng)站網(wǎng)店如何做推廣
  • 網(wǎng)站怎么做構(gòu)成網(wǎng)址查詢工具
  • 網(wǎng)站建設(shè)教程多少錢sem和seo有什么區(qū)別