溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

MySQL的事務(wù)隔離級別介紹

發(fā)布時間:2021-08-30 22:20:46 來源:億速云 閱讀:118 作者:chen 欄目:MySQL數(shù)據(jù)庫

本篇內(nèi)容介紹了“MySQL的事務(wù)隔離級別介紹”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!


什么是事務(wù)?

數(shù)據(jù)庫事務(wù)(簡稱:事務(wù))是數(shù)據(jù)庫管理系統(tǒng)執(zhí)行過程中的一個邏輯單位,由一個有限的數(shù)據(jù)庫操作序列構(gòu)成。—— 維基百科

事務(wù)的概念看上去不難,但是需要注意以下幾個點(diǎn):

1、首先,事務(wù)就是要保證一組數(shù)據(jù)庫操作,要么全部成功,要么全部失?。?/p>

2、在 MySQL 中,事務(wù)支持是在引擎層實(shí)現(xiàn)的;

3、并不是所有引擎都支持事務(wù),如 MyISAM 就不支持,InnoDB 就支持;


今天,我們的主角是隔離性,隔離性是指當(dāng)多個用戶并發(fā)操作數(shù)據(jù)庫時,數(shù)據(jù)庫為每一個用戶開啟不同的事務(wù),這些事務(wù)之間相互不干擾,相互隔離。

為什么需要隔離性?

如果事務(wù)之間不是互相隔離的,可能將會出現(xiàn)以下問題。

1、臟讀

臟讀(dirty read),簡單來說,就是一個事務(wù)在處理過程中讀取了另外一個事務(wù)未提交的數(shù)據(jù)。

這種未提交的數(shù)據(jù)我們稱之為臟數(shù)據(jù)。依據(jù)臟數(shù)據(jù)所做的操作肯能是不正確的。

還記得上節(jié)中我們提到的 dirty page 嗎?這種臨時處理的未提交的,都是「臟」的。

舉例

MySQL的事務(wù)隔離級別介紹

但是,若該事務(wù)未提交成功,最終所有操作都會回滾,小編看到的一分錢也只是鏡花水月。比如,你給小編贊賞 1 分錢,整個事務(wù)需要兩個步驟:

①給小編賬號加一分錢,這時小編看到了,覺得很欣慰;
②你的賬號減一分錢;

MySQL的事務(wù)隔離級別介紹

2、不可重復(fù)讀

不可重復(fù)讀(non-repeatable read),是指一個事務(wù)范圍內(nèi),多次查詢某個數(shù)據(jù),卻得到不同的結(jié)果。

在第一個事務(wù)中的兩次讀取數(shù)據(jù)之間,由于第二個事務(wù)的修改,第一個事務(wù)兩次讀到的數(shù)據(jù)可能就是不一樣的。

舉例

MySQL的事務(wù)隔離級別介紹

接著上一個例子,假設(shè)你真給小編打賞了一分錢,小編樂得屁顛屁顛地去準(zhǔn)備提現(xiàn),一查,發(fā)現(xiàn)真多了一分錢。

在這同時,在我還沒有提現(xiàn)成功之前,小編的老婆已經(jīng)提前將這一分錢支走了,小編此時再次查賬,發(fā)現(xiàn)一分錢也沒了。

MySQL的事務(wù)隔離級別介紹

臟讀和不可重復(fù)讀有點(diǎn)懵逼?

二者的區(qū)別是,臟讀是某一事務(wù)讀取了另外一個事務(wù)未提交的數(shù)據(jù),不可重復(fù)讀是讀取了其他事務(wù)提交的數(shù)據(jù)。

其實(shí),有些情況下,不可重復(fù)讀不是問題,比如,小編提現(xiàn)期間,一分錢被老婆支走了,這不是問題!

而臟讀,是可以通過設(shè)置隔離級別避免的。

3、幻讀

幻讀(phantom read),是事務(wù)非獨(dú)立執(zhí)行時發(fā)生的一種現(xiàn)象。

例如事務(wù) T1 對一個表中所有的行的某個數(shù)據(jù)項(xiàng)做了從“1”修改為“2”的操作,這時事務(wù) T2 又對這個表中插入了一行數(shù)據(jù)項(xiàng)為“1”的數(shù)據(jù),并且提交給數(shù)據(jù)庫。

而操作事務(wù) T1 的用戶如果再查看剛剛修改的數(shù)據(jù),會發(fā)現(xiàn)數(shù)據(jù)怎么還是 1?其實(shí)這行是從事務(wù) T2 中添加的,就好像產(chǎn)生幻覺一樣,這就是發(fā)生了幻讀。

舉例

MySQL的事務(wù)隔離級別介紹

其實(shí)上面的解釋已經(jīng)是一個例子了,但是還是要舉個例子。

比如,小編準(zhǔn)備提取你打賞的一分錢,提取完了,這時又有其他熱心網(wǎng)友打賞了一分錢,小編一看,明明已經(jīng)取出了,怎么又有一分錢???

小編此時以為像做夢一樣,我覺得也可以叫「夢讀」,哈哈。

幻讀和不可重復(fù)讀都是讀取了另一條已經(jīng)提交的事務(wù)(這點(diǎn)就臟讀不同),所不同的是不可重復(fù)讀查詢的都是同一個數(shù)據(jù)項(xiàng),而幻讀針對的是一批數(shù)據(jù)整體(比如數(shù)據(jù)的個數(shù))。

事務(wù)的隔離級別

為了解決上面可能出現(xiàn)的問題,我們就需要設(shè)置隔離級別,也就是事務(wù)之間按照什么規(guī)則進(jìn)行隔離,將事務(wù)隔離到什么程度。

首先,需要明白一點(diǎn),隔離程度越強(qiáng),事務(wù)的執(zhí)行效率越低。

ANSI/ISO SQL 定義了 4 種標(biāo)準(zhǔn)隔離級別:

① Serializable(串行化):花費(fèi)最高代價但最可靠的事務(wù)隔離級別。

“寫”會加“寫鎖”,“讀”會加“讀鎖”。當(dāng)出現(xiàn)讀寫鎖沖突的時候,后訪問的事務(wù)必須等前一個事務(wù)執(zhí)行完成,才能繼續(xù)執(zhí)行。

事務(wù) 100% 隔離,可避免臟讀、不可重復(fù)讀、幻讀的發(fā)生。

② Repeatable read(可重復(fù)讀,默認(rèn)級別):多次讀取同一范圍的數(shù)據(jù)會返回第一次查詢的快照,即使其他事務(wù)對該數(shù)據(jù)做了更新修改。事務(wù)在執(zhí)行期間看到的數(shù)據(jù)前后必須是一致的。

但如果這個事務(wù)在讀取某個范圍內(nèi)的記錄時,其他事務(wù)又在該范圍內(nèi)插入了新的記錄,當(dāng)之前的事務(wù)再次讀取該范圍的記錄時,會產(chǎn)生幻行,這就是幻讀。

可避免臟讀、不可重復(fù)讀的發(fā)生。但是可能會出現(xiàn)幻讀。

③ Read committed (讀已提交):保證一個事物提交后才能被另外一個事務(wù)讀取。另外一個事務(wù)不能讀取該事物未提交的數(shù)據(jù)。

可避免臟讀的發(fā)生,但是可能會造成不可重復(fù)讀。

大多數(shù)數(shù)據(jù)庫的默認(rèn)級別就是 Read committed,比如 Sql Server , Oracle。

④ Read uncommitted (讀未提交):最低的事務(wù)隔離級別,一個事務(wù)還沒提交時,它做的變更就能被別的事務(wù)看到。

任何情況都無法保證。

MySQL的事務(wù)隔離級別介紹隔離級別

下圖中是一個很好的例子,分別解釋了四種事務(wù)隔離級別下,事務(wù) B 能夠讀取到的結(jié)果。

MySQL的事務(wù)隔離級別介紹

看著還是有點(diǎn)懵逼?那我們再舉個例子。

A,B 兩個事務(wù),分別做了一些操作,操作過程中,在不同隔離級別下查看變量的值:

MySQL的事務(wù)隔離級別介紹

隔離級別是串行化,則在事務(wù) B 執(zhí)行「將 1 改成 2」的時候,會被鎖住。直到事務(wù) A 提交后,事務(wù) B 才可以繼續(xù)執(zhí)行。

再次總結(jié)

讀未提交:別人改數(shù)據(jù)的事務(wù)尚未提交,我在我的事務(wù)中也能讀到。
讀已提交:別人改數(shù)據(jù)的事務(wù)已經(jīng)提交,我在我的事務(wù)中才能讀到。
可重復(fù)讀:別人改數(shù)據(jù)的事務(wù)已經(jīng)提交,我在我的事務(wù)中也不去讀。
串行:我的事務(wù)尚未提交,別人就別想改數(shù)據(jù)。

這 4 種隔離級別,并行性能依次降低,安全性依次提高。

總的來說,事務(wù)隔離級別越高,越能保證數(shù)據(jù)的完整性和一致性,但是付出的代價卻是并發(fā)執(zhí)行效率的低下。


隔離級別的實(shí)現(xiàn)

事務(wù)的機(jī)制是通過視圖(read-view)來實(shí)現(xiàn)的并發(fā)版本控制(MVCC),不同的事務(wù)隔離級別創(chuàng)建讀視圖的時間點(diǎn)不同。

  • 可重復(fù)讀是每個事務(wù)重建讀視圖,整個事務(wù)存在期間都用這個視圖。

  • 讀已提交是每條 SQL 創(chuàng)建讀視圖,在每個 SQL 語句開始執(zhí)行的時候創(chuàng)建的。隔離作用域僅限該條 SQL 語句。

  • 讀未提交是不創(chuàng)建,直接返回記錄上的最新值

  • 串行化隔離級別下直接用加鎖的方式來避免并行訪問。

這里的視圖可以理解為數(shù)據(jù)副本,每次創(chuàng)建視圖時,將當(dāng)前已持久化的數(shù)據(jù)創(chuàng)建副本,后續(xù)直接從副本讀取,從而達(dá)到數(shù)據(jù)隔離效果。

隔離級別的實(shí)現(xiàn)

我們每一次的修改操作,并不是直接對行數(shù)據(jù)進(jìn)行操作。

比如我們設(shè)置 id 為 3 的行的 A 屬性為 10,并不是直接修改表中的數(shù)據(jù),而是新加一行。

同時數(shù)據(jù)表其實(shí)還有一些隱藏的屬性,比如每一行的事務(wù) id,所以每一行數(shù)據(jù)可能會有多個版本,每一個修改過它的事務(wù)都會有一行,并且還會有關(guān)聯(lián)的 undo 日志,表示這個操作原來的數(shù)據(jù)是什么,可以用它做回滾。

那么為什么要這么做?

因?yàn)槿绻覀冎苯影褦?shù)據(jù)修改了,那么其他事務(wù)就用不了原先的值了,違反了事務(wù)的一致性。

那么一個事務(wù)讀取某一行的數(shù)據(jù)到底返回什么結(jié)果呢?

取決于隔離級別,如果是 Read Committed,那么返回的是最新的事務(wù)的提交值,所以未提交的事務(wù)修改的值是不會讀到的,這就是 Read Committed 實(shí)現(xiàn)的原理。

如果是 Read Repeatable 級別,那么只能返回發(fā)起時間比當(dāng)前事務(wù)早的事務(wù)的提交值,和比當(dāng)前事務(wù)晚的刪除事務(wù)刪除的值。這其實(shí)就是 MVCC 方式。

undo log

undo log 中存儲的是老版本數(shù)據(jù)。假設(shè)修改表中 id=2 的行數(shù)據(jù),把 Name='B' 修改為 Name = 'B2' ,那么 undo 日志就會用來存放 Name='B' 的記錄,如果這個修改出現(xiàn)異常,可以使用 undo 日志來實(shí)現(xiàn)回滾操作,保證事務(wù)的一致性。

當(dāng)一個舊的事務(wù)需要讀取數(shù)據(jù)時,為了能讀取到老版本的數(shù)據(jù),需要順著 undo 鏈找到滿足其可見性的記錄。當(dāng)版本鏈很長時,通??梢哉J(rèn)為這是個比較耗時的操作。

假設(shè)一個值從 1 被按順序改成了 2、3、4,在回滾日志里面就會有類似下面的記錄。

MySQL的事務(wù)隔離級別介紹

當(dāng)前值是 4,但是在查詢這條記錄的時候,不同時刻啟動的事務(wù)會有不同的 read-view。

如圖中看到的,在視圖 A、B、C 里面,這一個記錄的值分別是 1、2、4,同一條記錄在系統(tǒng)中可以存在多個版本,就是數(shù)據(jù)庫的多版本并發(fā)控制(MVCC)。對于 read-view A,要得到 1,就必須將當(dāng)前值依次執(zhí)行圖中所有的回滾操作得到。

同時你會發(fā)現(xiàn),即使現(xiàn)在有另外一個事務(wù)正在將 4 改成 5,這個事務(wù)跟 read-view A、B、C 對應(yīng)的事務(wù)是不會沖突的。


另外,在回滾段中的 undo log 分為: insert undo log 和 update undo log:

  • insert undo log : 事務(wù)對 insert 新記錄時產(chǎn)生的 undolog,只在事務(wù)回滾時需要,并且在事務(wù)提交后就可以立即丟棄。(誰會對剛插入的數(shù)據(jù)有可見性需求呢?。。?/p>

  • update undo log : 事務(wù)對記錄進(jìn)行 delete 和 update 操作時產(chǎn)生的 undo log。不僅在事務(wù)回滾時需要,一致性讀也需要,所以不能隨便刪除,只有當(dāng)數(shù)據(jù)庫所使用的快照中不涉及該日志記錄,對應(yīng)的回滾日志才會被 purge 線程刪除。

何時刪除?

在不需要的時候才刪除。也就是說,系統(tǒng)會判斷,當(dāng)沒有事務(wù)再需要用到這些回滾日志時,回滾日志會被刪除。

就是當(dāng)系統(tǒng)里沒有比這個回滾日志更早的 read-view 的時候。

長事務(wù)

直觀感覺,一個事務(wù)花費(fèi)很長時間不能夠結(jié)束,就是一個長的事務(wù),簡稱長事務(wù)(Long Transaction)。

長事務(wù)是數(shù)據(jù)庫用戶經(jīng)常會碰到且是非常令人頭疼的問題。長事務(wù)處理需要恰當(dāng)進(jìn)行,如處理不當(dāng)可能引起數(shù)據(jù)庫的崩潰,為用戶帶來不必要的損失。

根據(jù)上面的論述,長事務(wù)意味著系統(tǒng)里面會存在很老的事務(wù)視圖。

由于這些事務(wù)隨時可能訪問數(shù)據(jù)庫里面的任何數(shù)據(jù),所以這個事務(wù)提交之前,數(shù)據(jù)庫里面它可能用到的 undo log 都必須保留,這就會導(dǎo)致大量占用存儲空間。

在 MySQL 5.5 及以前的版本,回滾日志是跟數(shù)據(jù)字典一起放在 ibdata 文件里的,即使長事務(wù)最終提交,回滾段被清理,文件也不會變小

除了對回滾段的影響,長事務(wù)還占用鎖資源,也可能拖垮整個庫,這個我們會在后面講鎖的時候展開。

因此,我們要盡量避免長事務(wù)。

“MySQL的事務(wù)隔離級別介紹”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI