<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          搞懂MySQL的鎖、MVCC、事務(wù)隔離級(jí)別,看這篇就夠了!

          共 7660字,需瀏覽 16分鐘

           ·

          2021-08-25 06:46

          “本文略長(zhǎng),但值得認(rèn)真研讀兩遍!!!“


          在如今互聯(lián)網(wǎng)業(yè)務(wù)中使用范圍最廣的數(shù)據(jù)庫(kù)無(wú)疑還是關(guān)系型數(shù)據(jù)庫(kù)MySQL,之所以用"還是"這個(gè)詞,是因?yàn)樽罱鼛啄陣?guó)內(nèi)數(shù)據(jù)庫(kù)領(lǐng)域也取得了一些長(zhǎng)足進(jìn)步,例如以TIDB、OceanBase等為代表的分布式數(shù)據(jù)庫(kù),但它們暫時(shí)還沒(méi)有形成絕對(duì)的覆蓋面,所以現(xiàn)階段還得繼續(xù)學(xué)習(xí)MySQL數(shù)據(jù)庫(kù)以應(yīng)對(duì)工作中遇到的一些問(wèn)題,以及面試過(guò)程中關(guān)于數(shù)據(jù)庫(kù)部分的考察。


          今天的內(nèi)容就和大家聊一聊MySQL數(shù)據(jù)庫(kù)中關(guān)于并發(fā)控制、事務(wù)以及存儲(chǔ)引擎這幾個(gè)最核心的問(wèn)題。本內(nèi)容涉及的知識(shí)圖譜如下圖所示:



          并發(fā)控制



          并發(fā)控制是一個(gè)內(nèi)容龐大的話題,在計(jì)算機(jī)軟件系統(tǒng)中只要在同一時(shí)刻存在多個(gè)請(qǐng)求同時(shí)修改數(shù)據(jù)的情況,就都會(huì)產(chǎn)生并發(fā)控制的問(wèn)題,例如Java中的多線程安全問(wèn)題等。在MySQL中的并發(fā)控制,主要是討論數(shù)據(jù)庫(kù)如何控制表數(shù)據(jù)的并發(fā)讀寫。


          例如有一張表useraccount,其結(jié)構(gòu)如下:

          此時(shí)如果有如下兩條SQL語(yǔ)句同一時(shí)刻向數(shù)據(jù)庫(kù)發(fā)起請(qǐng)求:

          SQL-A:

          update useraccount t set t.account=t.account+100 where username='wudimanong';

          SQL-B:

          update useraccount t set t.account=t.account-100 where username='wudimanong'


          當(dāng)上述語(yǔ)句都執(zhí)行完成,正確結(jié)果應(yīng)該是account=100,但在并發(fā)情況下,卻有可能發(fā)生這樣的情況:



          那么在MySQL中是如何進(jìn)行并發(fā)控制的呢?實(shí)際上與大多數(shù)并發(fā)控制方式一樣,在MySQL中也是利用鎖機(jī)制來(lái)實(shí)現(xiàn)并發(fā)控制的。

          01 MySQL鎖類型


          在MySQL中主要是通過(guò)"讀寫鎖"來(lái)實(shí)現(xiàn)并發(fā)控制。


          讀鎖(read lock):也叫共享鎖(share lock),多個(gè)讀請(qǐng)求可以同時(shí)共享一把鎖來(lái)讀取數(shù)據(jù),而不會(huì)造成阻塞。


          寫鎖(write lock):也叫排他鎖(exclusive lock),寫鎖會(huì)排斥其他所有獲取鎖的請(qǐng)求,一直阻塞,直到完成寫入并釋放鎖。


          讀寫鎖可以做到讀讀并行,但是無(wú)法做到寫讀、寫寫并行。后面會(huì)講到的事務(wù)隔離性就是根據(jù)讀寫鎖來(lái)實(shí)現(xiàn)的!


          02 MySQL鎖粒度


          上面提及的讀寫鎖是根據(jù)MySQL的鎖類型來(lái)劃分的,而讀寫鎖能夠施加的粒度在數(shù)據(jù)庫(kù)中主要體現(xiàn)為表和行,也稱為表鎖(table lock)、行鎖(row lock)。


          表鎖(table lock):是MySQL中最基本的鎖策略,它會(huì)鎖定整張表,這樣維護(hù)鎖的開(kāi)銷最小,但是會(huì)降低表的讀寫效率。如果一個(gè)用戶通過(guò)表鎖來(lái)實(shí)現(xiàn)對(duì)表的寫操作(插入、刪除、更新),那么先需要獲得鎖定該表的寫鎖,那么在這種情況下,其他用戶對(duì)該表的讀寫都會(huì)被阻塞。一般情況下"alter table"之類的語(yǔ)句才會(huì)使用表鎖。


          行鎖(row lock):行鎖可以最大程度地支持并發(fā)讀寫,但數(shù)據(jù)庫(kù)維護(hù)鎖的開(kāi)銷會(huì)比較大。行鎖是我們?nèi)粘J褂米疃嗟逆i策略,一般情況下MySQL中的行級(jí)鎖由具體的存儲(chǔ)引擎實(shí)現(xiàn),而不是MySQL服務(wù)器層面去實(shí)現(xiàn)(表鎖MySQL服務(wù)器層面會(huì)實(shí)現(xiàn))。


          03 多版本并發(fā)控制(MVCC)


          MVCC(MultiVersion Concurrency Control),多版本并發(fā)控制。在MySQL的大多數(shù)事務(wù)引擎(如InnoDB)中,都不只是簡(jiǎn)單地實(shí)現(xiàn)了行級(jí)鎖,否則會(huì)出現(xiàn)這樣的情況:"數(shù)據(jù)A被某個(gè)用戶更新期間(獲取行級(jí)寫鎖),其他用戶讀取該條數(shù)據(jù)(獲取讀鎖)都會(huì)被阻塞“。但現(xiàn)實(shí)情況顯然不是這樣,這是因?yàn)镸ySQL的存儲(chǔ)引擎基于提升并發(fā)性能的考慮,通過(guò)MVCC數(shù)據(jù)多版本控制,做到了讀寫分離,從而實(shí)現(xiàn)不加鎖讀取數(shù)據(jù)進(jìn)而做到了讀寫并行。


          以InnoDB存儲(chǔ)引擎的MVCC實(shí)現(xiàn)為例:

          InnoDB的MVCC,是通過(guò)在每行記錄后面保存兩個(gè)隱藏的列來(lái)實(shí)現(xiàn)的。這兩個(gè)列,一個(gè)保存了行的創(chuàng)建時(shí)間,一個(gè)保存了行的過(guò)期時(shí)間。當(dāng)然它們存儲(chǔ)的并不是實(shí)際的時(shí)間值,而是系統(tǒng)版本號(hào)。每開(kāi)啟一個(gè)新的事務(wù),系統(tǒng)版本號(hào)都會(huì)自動(dòng)遞增;事務(wù)開(kāi)始時(shí)刻的系統(tǒng)版本號(hào)會(huì)作為事務(wù)的版本號(hào),用來(lái)和查詢到的每行記錄的版本號(hào)進(jìn)行比較。


          MVCC在MySQL中實(shí)現(xiàn)所依賴的手段主要是:"undo log和read view"。


          • undo log :undo log 用于記錄某行數(shù)據(jù)的多個(gè)版本的數(shù)據(jù)。

          • read view :用來(lái)判斷當(dāng)前版本數(shù)據(jù)的可見(jiàn)性


          undo log在后面講述事務(wù)還會(huì)介紹到。關(guān)于MVCC的讀寫原理示意圖如下:



          上圖演示了MySQL InnoDB存儲(chǔ)引擎,在REPEATABLE READ(可重復(fù)讀)事務(wù)隔離級(jí)別下,通過(guò)額外保存兩個(gè)系統(tǒng)版本號(hào)(行創(chuàng)建版本號(hào)、行刪除版本號(hào))實(shí)現(xiàn)MVCC,從而使得大多數(shù)讀操作都可以不用再加讀鎖。這樣的設(shè)計(jì)使得數(shù)據(jù)讀取操作更加簡(jiǎn)單、性能更好。


          那么在MVCC模式下數(shù)據(jù)讀取操作是如何保證數(shù)據(jù)讀取正確的呢?以InnoDB為例,Select時(shí)會(huì)根據(jù)以下兩個(gè)條件檢查每行記錄:


          • 只查找版本號(hào)小于或等于當(dāng)前事務(wù)版本的數(shù)據(jù)行,這樣可以確保事務(wù)讀取的行要么是在事務(wù)開(kāi)始前已經(jīng)存在,要么是事務(wù)自身插入或者修過(guò)的。

          • 行的刪除版本號(hào)要么未定義,要么大于當(dāng)前事務(wù)版本號(hào)。這樣可以確保事務(wù)讀取到的行,在事務(wù)開(kāi)始之前未被刪除。


          只有符合上述兩個(gè)條件的記錄,才能返回作為查詢的結(jié)果!以圖中示范的邏輯為例,寫請(qǐng)求將account變更為200的過(guò)程中,InnoDB會(huì)再插入一行新記錄(account=200),并將當(dāng)前系統(tǒng)版本號(hào)作為行創(chuàng)建版本號(hào)(createVersion=2),同時(shí)將當(dāng)前系統(tǒng)版本號(hào)作為原來(lái)行的行刪除版本號(hào)(deleteVersion=2),那么此時(shí)關(guān)于這條數(shù)據(jù)有兩個(gè)版本的數(shù)據(jù)副本,具體如下:


          假如現(xiàn)在寫操作還未結(jié)束,事務(wù)對(duì)其他用戶暫不可見(jiàn),按照Select檢查條件只有accout=100的記錄才符合條件,因此查詢結(jié)果會(huì)返回account=100的記錄!


          上述過(guò)程就是InnoDB存儲(chǔ)引擎關(guān)于MVCC實(shí)現(xiàn)的基本原理,但是后面需要注意MVCC多版本并發(fā)控制的邏輯只能工作在“REPEATABLE READ(可重復(fù)讀)和READ COMMITED(提交讀)”兩種事務(wù)隔離級(jí)別下。其他兩個(gè)隔離級(jí)別都與MVCC不兼容,因?yàn)?em>READ UNCOMMITED(未提交讀)總是讀取最新的數(shù)據(jù)行,而不是符合當(dāng)前事務(wù)版本的數(shù)據(jù)行;而SERIALIZABLE則會(huì)對(duì)所有讀取的行都加鎖,也不符合MVCC的思想。


          MySQL事務(wù)



          前面在講解了關(guān)于MySQL并發(fā)控制的過(guò)程中,也提到了事務(wù)相關(guān)的內(nèi)容,接下來(lái)我們來(lái)更全面的梳理下關(guān)于事務(wù)的核心知識(shí)。


          相信大家在日常的開(kāi)發(fā)過(guò)程中,都使用過(guò)數(shù)據(jù)庫(kù)事務(wù),對(duì)事務(wù)的特點(diǎn)也都能張口就來(lái)——ACID。那么事務(wù)內(nèi)部到底是怎么實(shí)現(xiàn)的呢?在接下來(lái)的內(nèi)容中,就來(lái)和大家具體聊一聊這個(gè)問(wèn)題!


          01 事務(wù)概述


          數(shù)據(jù)庫(kù)事務(wù)本身所要達(dá)成的效果主要體現(xiàn)在:"可靠性"以及"并發(fā)處理"這兩個(gè)方面。


          • 可靠性:數(shù)據(jù)庫(kù)要保證當(dāng)insert或update操作拋出異常,或者數(shù)據(jù)庫(kù)crash的時(shí)候要保障數(shù)據(jù)操作的前后一致。

          • 并發(fā)處理:說(shuō)的是當(dāng)多個(gè)并發(fā)請(qǐng)求過(guò)來(lái),并且其中有一個(gè)請(qǐng)求是對(duì)數(shù)據(jù)進(jìn)行修改操作,為了避免其他請(qǐng)求讀到臟數(shù)據(jù),需要對(duì)事務(wù)之間的讀寫進(jìn)行隔離。


          實(shí)現(xiàn)MySQL數(shù)據(jù)庫(kù)事務(wù)功能主要有三個(gè)技術(shù),分別是日志文件(redo log和undo log)、鎖技術(shù)及MVCC。


          02 redo log與undo log


          redo log與undo log是實(shí)現(xiàn)MySQL事務(wù)功能的核心技術(shù)。


          1)、redo log


          redo log叫做重做日志,是實(shí)現(xiàn)事務(wù)持久性的關(guān)鍵。redo log日志文件主要由2部分組成:重做日志緩沖(redo log buffer)、重做日志文件(redo log file)。


          在MySql中為了提升數(shù)據(jù)庫(kù)性能并不會(huì)把每次的修改都實(shí)時(shí)同步到磁盤,而是會(huì)先存到一個(gè)叫做“Boffer Pool”的緩沖池中,之后會(huì)再使用后臺(tái)線程去實(shí)現(xiàn)緩沖池和磁盤之間的同步。


          如果采取這樣的模式,可能會(huì)出現(xiàn)這樣的問(wèn)題:如果在數(shù)據(jù)還沒(méi)來(lái)得及同步的情況下出現(xiàn)宕機(jī)或斷電,那么就可能會(huì)丟失部分已提交事務(wù)的修改信息!而這種情況對(duì)于數(shù)據(jù)庫(kù)軟件來(lái)說(shuō)是不可以接受的。


          所以redo log的主要作用就是用來(lái)記錄已成功提交事務(wù)的修改信息,并且會(huì)在事務(wù)提交后實(shí)時(shí)將redo log持久化到磁盤,這樣在系統(tǒng)重啟之后就可以讀取redo log來(lái)恢復(fù)最新的數(shù)據(jù)。


          接下來(lái)我們以前面SQL-A所開(kāi)啟的事務(wù)為例來(lái)演示redo log的具體是如何運(yùn)行的,如下圖所示:



          如上圖所示,當(dāng)修改一行記錄的事務(wù)開(kāi)啟,MySQL存儲(chǔ)引擎是把數(shù)據(jù)從磁盤讀取到內(nèi)存的緩沖池上進(jìn)行修改,這個(gè)時(shí)候數(shù)據(jù)在內(nèi)存中被修改后就與磁盤中的數(shù)據(jù)產(chǎn)生了差異,這種有差異的數(shù)據(jù)也被稱之為“臟頁(yè)”。


          而一般存儲(chǔ)引擎對(duì)于臟頁(yè)的處理并不是每次生成臟頁(yè)就即刻將臟頁(yè)刷新回磁盤,而是通過(guò)后臺(tái)線程“master thread”以大致每秒運(yùn)行一次或每10秒運(yùn)行一次的頻率去刷新磁盤。在這種情況下,出現(xiàn)數(shù)據(jù)庫(kù)宕機(jī)或斷電等情況,那么尚未刷新回磁盤的數(shù)據(jù)就有可能丟失。


          而redo log日志的作用就是為了調(diào)和內(nèi)存與磁盤的速度差異。當(dāng)事務(wù)被提交時(shí),存儲(chǔ)引擎會(huì)首先將要修改的數(shù)據(jù)寫入redo log,然后再去修改緩沖池中真正的數(shù)據(jù)頁(yè),并實(shí)時(shí)刷新一次數(shù)據(jù)同步。如果在這個(gè)過(guò)程中,數(shù)據(jù)庫(kù)掛了,由于redo log物理日志文件已經(jīng)記錄了事務(wù)修改,所以在數(shù)據(jù)庫(kù)重啟后就可以根據(jù)redo log日志進(jìn)行事務(wù)數(shù)據(jù)恢復(fù)。


          2)、undo log


          上面我們聊了redo log日志,它的作用主要是用來(lái)恢復(fù)數(shù)據(jù),保障已提交事務(wù)的持久化特性。在MySQL中還有另外一種非常重要的日志類型undo log,又叫回滾日志,它主要是用于記錄數(shù)據(jù)被修改前的信息,這與記錄數(shù)據(jù)被修改后信息的redo log日志正好相反。


          undo log 主要記錄事務(wù)修改之前版本的數(shù)據(jù)信息,假如由于系統(tǒng)錯(cuò)誤或者rollback操作而回滾的話就可以根據(jù)undo log日志來(lái)將數(shù)據(jù)回滾到?jīng)]被修改之前的狀態(tài)。


          每次寫入數(shù)據(jù)或者修改數(shù)據(jù)之前存儲(chǔ)引擎都會(huì)將修改前的信息記錄到undo log。


          03 事務(wù)的實(shí)現(xiàn)


          前面我們講到了鎖、多版本并發(fā)控制(MVCC)、重做日志(redo log)以及回滾日志(undo log),這些內(nèi)容就是MySQL實(shí)現(xiàn)數(shù)據(jù)庫(kù)事務(wù)的基礎(chǔ)。從事務(wù)的四大特性來(lái)說(shuō),其對(duì)應(yīng)關(guān)系主要體現(xiàn)如下:

          實(shí)際上事務(wù)原子性、持久性、隔離性的最終目的都是為了確保事務(wù)數(shù)據(jù)的一致性。而ACID只是個(gè)概念,事務(wù)的最終目的是要保障數(shù)據(jù)的可靠性和一致性。


          接下來(lái)我們?cè)倬唧w分析下事務(wù)ACID特性的實(shí)現(xiàn)原理。


          1)、原子性的實(shí)現(xiàn)


          原子性,是指一個(gè)事務(wù)必須被視為不可分割的最小單位,一個(gè)事務(wù)中的所有操作要么全部執(zhí)行成功、要么全部失敗回滾,對(duì)一個(gè)事務(wù)來(lái)說(shuō)不可能只執(zhí)行其中的部分操作,這就是事務(wù)原子性的概念。


          而MySQL數(shù)據(jù)庫(kù)實(shí)現(xiàn)原子性的主要是通過(guò)回滾操作來(lái)實(shí)現(xiàn)的。所謂回滾操作就是當(dāng)發(fā)生錯(cuò)誤異?;蛘唢@示地執(zhí)行rollback語(yǔ)句時(shí)需要把數(shù)據(jù)還原到原先的模樣,而這個(gè)過(guò)程就需要借助undo log來(lái)進(jìn)行。具體規(guī)則如下:


          • 每條數(shù)據(jù)變更(insert/update/delete)操作都伴隨著一條undo log的生成,并且回滾日志必須先于數(shù)據(jù)持久化到磁盤上;

          • 所謂的回滾就是根據(jù)undo log日志做逆向操作,比如delete的逆向操作為insert,insert的逆向操作為delete,update的逆向操作為update等;


          2)、持久性的實(shí)現(xiàn)


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


          事務(wù)的持久性主要是通過(guò)redo log日志來(lái)實(shí)現(xiàn)的。redo log日志之所以能夠彌補(bǔ)緩存同步所造成的數(shù)據(jù)差異,主要其具備以下特點(diǎn):


          • redo log的存儲(chǔ)是順序的,而緩存同步則是隨機(jī)操作;

          • 緩存同步是以數(shù)據(jù)頁(yè)為單位,每次傳輸?shù)臄?shù)據(jù)大小大于redo log;


          關(guān)于redo log實(shí)現(xiàn)事務(wù)持久性的邏輯可參考本文前面關(guān)于redo log部分的內(nèi)容!


          3)、隔離性的實(shí)現(xiàn)


          隔離性是事務(wù)ACID特性中最復(fù)雜的一個(gè)。在SQL標(biāo)準(zhǔn)里定義了四種隔離級(jí)別,每一種隔離級(jí)別都規(guī)定一個(gè)事務(wù)中的修改,那些是事務(wù)之間可見(jiàn)的,那些是不可見(jiàn)的。


          MySQL隔離級(jí)別有以下四種(級(jí)別由低到高):


          • READ UNCOMMITED (未提交讀);

          • READ COMMITED (提交讀)

          • REPEATABLE READ (可重復(fù)讀)

          • SERIALIZABLE (可串行化)


          隔離級(jí)別越低,則數(shù)據(jù)庫(kù)可以執(zhí)行的并發(fā)度越高,但是實(shí)現(xiàn)的復(fù)雜度和開(kāi)銷也越大。只要徹底理解了隔離級(jí)別以及它的實(shí)現(xiàn)原理,就相當(dāng)于理解了ACID中的事務(wù)隔離性。


          前面提到過(guò),原子性、持久性、隔離性的目的最終都是為了實(shí)現(xiàn)數(shù)據(jù)的一致性,但隔離性與其它兩個(gè)有所區(qū)別,原子性和持久性主要是為了保障數(shù)據(jù)的可靠性,比如做到宕機(jī)后的數(shù)據(jù)恢復(fù),以及錯(cuò)誤后的數(shù)據(jù)回滾。而隔離性的核心目標(biāo)則是要管理多個(gè)并發(fā)讀寫請(qǐng)求的訪問(wèn)順序,實(shí)現(xiàn)數(shù)據(jù)庫(kù)數(shù)據(jù)的安全和高效訪問(wèn),實(shí)質(zhì)上就是一場(chǎng)數(shù)據(jù)的安全性與性能之間的權(quán)衡游戲。


          可靠性高的隔離級(jí)別,并發(fā)性能低(例如SERIALIZABLE隔離級(jí)別,因?yàn)樗械淖x寫都會(huì)加鎖);而可靠性低的,并發(fā)性能高(例如READ UNCOMMITED,因?yàn)樽x寫完全不加鎖)。


          接下來(lái)我們?cè)俜謩e分析下這四種隔離級(jí)別的特點(diǎn):


          READ UNCOMMITTED


          在READ UNCOMMITTED隔離級(jí)別下,一個(gè)事務(wù)中的修改即使還沒(méi)有提交,對(duì)其它事務(wù)也是可見(jiàn),也就是說(shuō)事務(wù)可以讀取到未提交的數(shù)據(jù)。


          因?yàn)樽x不會(huì)添加鎖,所以寫操作在讀的過(guò)程中修改數(shù)據(jù)的話會(huì)造成"臟讀"。未提交讀隔離級(jí)別讀寫示意圖如下:



          如上圖所示,寫請(qǐng)求將account修改為200,此時(shí)事務(wù)未提交;但是讀請(qǐng)求可以讀取到未提交的事務(wù)數(shù)據(jù)account=200;隨后寫請(qǐng)求事務(wù)失敗回滾account=100;那么此時(shí)讀請(qǐng)求讀取的account=200的數(shù)據(jù)就是臟數(shù)據(jù)。


          這種隔離級(jí)別的優(yōu)點(diǎn)是讀寫并行、性能高;但是缺點(diǎn)是容易造成臟讀。所以在MySQL數(shù)據(jù)庫(kù)中一般情況下并不會(huì)采取此種隔離級(jí)別!


          READ COMMITED


          這種事務(wù)隔離級(jí)別也叫"不可重復(fù)讀或提交讀"。它的特點(diǎn)是一個(gè)事務(wù)在它提交之前的所有修改,其它事務(wù)都是不可見(jiàn)的;其它事務(wù)只能讀到已提交的修改變化。


          這種隔離級(jí)別看起來(lái)很完美,也符合大部分邏輯場(chǎng)景,但該事務(wù)隔離級(jí)別會(huì)產(chǎn)生"不可重讀""幻讀"的問(wèn)題。


          不可重讀:是指一個(gè)事務(wù)內(nèi)多次讀取的相同行的數(shù)據(jù),結(jié)果卻不一樣。例如事務(wù)A讀取a行數(shù)據(jù),而事務(wù)B此時(shí)修改了a行的數(shù)據(jù)并提交了事務(wù),那么事務(wù)A在下一次讀取a行數(shù)據(jù)時(shí),發(fā)現(xiàn)和第一次不一樣了!


          幻讀:是指一個(gè)事務(wù)按照相同的查詢條件檢索數(shù)據(jù),但是多次檢索出的數(shù)據(jù)結(jié)果卻不一樣。例如事務(wù)A第一次以條件x=0檢索數(shù)據(jù)獲取了5條記錄;此時(shí)事務(wù)B向表中插入了一條x=0的數(shù)據(jù)并提交了事務(wù);那么事務(wù)A第二次再以條件x=0檢索數(shù)據(jù)時(shí),發(fā)現(xiàn)獲取了6條記錄!


          那么在READ COMMITED隔離級(jí)別下為什么會(huì)產(chǎn)生不可重復(fù)讀和幻讀的問(wèn)題呢?


          實(shí)際上不可重復(fù)讀事務(wù)隔離級(jí)別也采用了我們前面講過(guò)的MVCC(多版本并發(fā)控制)機(jī)制。但在READ COMMITED隔離級(jí)別下的MVCC機(jī)制,會(huì)在每次select的時(shí)候都生成一個(gè)新的系統(tǒng)版本號(hào),所以事務(wù)中每次select操作讀到的不是一個(gè)副本而是不同的副本數(shù)據(jù),所以在每次select之間,如果有其它事務(wù)更新并提交了我們讀取的數(shù)據(jù),那么就會(huì)產(chǎn)生不可重復(fù)讀和幻讀的現(xiàn)象。


          不可重復(fù)讀產(chǎn)生的原因示意圖如下:



          REPEATABLE READ


          事務(wù)隔離級(jí)別REPEATABLE READ,也叫可重復(fù)讀,它是MySQL數(shù)據(jù)庫(kù)的默認(rèn)事務(wù)隔離級(jí)別。在這種事務(wù)隔離級(jí)別下,一個(gè)事務(wù)內(nèi)的多次讀取結(jié)果是一致的,這種隔離級(jí)別可以避免臟讀、不可重復(fù)讀等查詢問(wèn)題。


          這種事務(wù)隔離級(jí)別的實(shí)現(xiàn)手段主要是采用讀寫鎖+MVCC機(jī)制。具體示意圖如下:



          如上圖所示,在該事務(wù)隔離級(jí)別下的MVCC機(jī)制,并不會(huì)在事務(wù)內(nèi)每次查詢都產(chǎn)生一個(gè)新的系統(tǒng)版本號(hào),所以一個(gè)事務(wù)內(nèi)的多次查詢,數(shù)據(jù)副本都是一個(gè),因此不會(huì)產(chǎn)生不可重復(fù)讀問(wèn)題。關(guān)于此隔離級(jí)別下MVCC更多的細(xì)節(jié)可參考前面內(nèi)容!


          但是需要注意,此隔離級(jí)別解決了不可重復(fù)讀的問(wèn)題,但是并沒(méi)有解決幻讀的問(wèn)題,所以如果事務(wù)A中存在條件查詢,另外一個(gè)事務(wù)B在此期間新增或刪除了該條件的數(shù)據(jù)并提交了事務(wù),那么依然會(huì)造成事務(wù)A產(chǎn)生幻讀。所以在使用MySQL時(shí)需要注意這個(gè)問(wèn)題!


          SERIALIZABLE


          該隔離級(jí)別理解起來(lái)最簡(jiǎn)單,因?yàn)樗x寫請(qǐng)求都會(huì)加排他鎖,所以不會(huì)造成任何數(shù)據(jù)不一致的問(wèn)題,就是性能不高,所以采用此隔離級(jí)別的數(shù)據(jù)庫(kù)很少!


          4)、一致性的實(shí)現(xiàn)


          一致性主要是指通過(guò)回滾、恢復(fù)以及在并發(fā)條件下的隔離性來(lái)實(shí)現(xiàn)數(shù)據(jù)庫(kù)數(shù)據(jù)的一致!前面所講述的原子性、持久性及隔離性最終就是為了實(shí)現(xiàn)一致性!



          MySQL存儲(chǔ)引擎



          前面的內(nèi)容我們分別講述了MySQL并發(fā)控制和事務(wù)的內(nèi)容,而實(shí)際上在并發(fā)控制和事務(wù)的具體細(xì)節(jié)都是依賴于MySql存儲(chǔ)引擎來(lái)實(shí)現(xiàn)的。MySQL最重要、最與眾不同的特性就是它的存儲(chǔ)引擎架構(gòu),這種將數(shù)據(jù)處理和存儲(chǔ)分離的架構(gòu)設(shè)計(jì)使得用戶在使用時(shí)可以根據(jù)性能、特性以及其它具體需求來(lái)選擇相應(yīng)的存儲(chǔ)引擎。


          雖然如此,但絕大部分情況下使用MySQL數(shù)據(jù)庫(kù)時(shí)選擇的還是InnoDB存儲(chǔ)引擎,不過(guò)這并不妨礙我們適當(dāng)?shù)亓私庀缕渌鎯?chǔ)引擎的特點(diǎn)。接下來(lái)給大家簡(jiǎn)單總結(jié)下,具體如下:

          以上我們簡(jiǎn)單總結(jié)了MySQL各種存儲(chǔ)引擎的大概特點(diǎn)及其大致適用的場(chǎng)景,但實(shí)際上除了InnoDB存儲(chǔ)引擎外,在互聯(lián)網(wǎng)業(yè)務(wù)中很少會(huì)看到其它存儲(chǔ)引擎的身影。雖然MySQL內(nèi)置了多種針對(duì)特定場(chǎng)景的存儲(chǔ)引擎,但是它們大多都有相應(yīng)的替代技術(shù),例如日志類應(yīng)用現(xiàn)在有Elasticsearch、而數(shù)倉(cāng)類應(yīng)用現(xiàn)在則有Hive、HBase等產(chǎn)品,至于內(nèi)存數(shù)據(jù)庫(kù)有MangoDB、Redis等NoSQL數(shù)據(jù)產(chǎn)品,所以能夠給MySQL發(fā)揮的也只有InnoDB了!


          PS:以上就是本文的全部?jī)?nèi)容希望能夠?qū)δ阌兴鶐椭?/span>



          —————END—————



          參考文檔:

          https://mp.weixin.qq.com/s/IvQWnts592KlDCnoXuP7DQ

          瀏覽 86
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本特一级免费 | 午夜操操| 大香蕉伊人综合在线 | 欧美成人性爱诱惑 | 国产一站免费 |