主頁(yè) > 知識(shí)庫(kù) > MySQL如何實(shí)現(xiàn)事務(wù)的ACID

MySQL如何實(shí)現(xiàn)事務(wù)的ACID

熱門標(biāo)簽:許昌外呼增值業(yè)務(wù)線路 石家莊400電話辦理公司 申請(qǐng)400電話電話價(jià)格 臨沂做地圖標(biāo)注 廣東400企業(yè)電話申請(qǐng)流程 地圖標(biāo)注客戶付款 咸陽(yáng)防封電銷卡 宜賓全自動(dòng)外呼系統(tǒng)廠家 新鄉(xiāng)智能外呼系統(tǒng)好處

前言

最近在面試,有被問(wèn)到,MySQL的InnoDB引擎是如何實(shí)現(xiàn)事務(wù)的,又或者說(shuō)是如何實(shí)現(xiàn)ACID這幾個(gè)特性的,當(dāng)時(shí)沒(méi)有答好,所以自己總結(jié)出來(lái),記錄一下。

事務(wù)的四大特性ACID

事務(wù)的四大特性ACID分別是,A-原子性(Atomicity),C-一致性(Consistency),I-隔離性(Isolation),D-持久性(Durability)。一致性是最終目的,原子性、隔離性、持久性是為了保證一致性所做的措施。所以我寫的順序并不是按照ACID來(lái)寫的,將一致性放到了最后,順序就變成了,ADIC。

原子性(A)

原子性是指一個(gè)事務(wù)就是一個(gè)不可分割的工作單位,要么全部都執(zhí)行成功,要么全部都執(zhí)行失敗,沒(méi)有中間狀態(tài)或是只執(zhí)行一部分。

MySQL的InnoDB引擎是靠undo log(回滾日志)來(lái)實(shí)現(xiàn)的,undo log能夠保證在事務(wù)回滾時(shí),能夠撤銷所有已經(jīng)執(zhí)行成功的SQL。

undo log 屬于邏輯日志,它記錄的是SQL執(zhí)行相關(guān)的信息。當(dāng)事務(wù)對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改時(shí),InnoDB會(huì)生成與之對(duì)應(yīng)的undo log。如果事務(wù)執(zhí)行失敗或者調(diào)用的rollback,導(dǎo)致事務(wù)需要回滾,InnoDB引擎會(huì)根據(jù)undo log中的記錄,將數(shù)據(jù)回滾到之前的樣子。
例如在執(zhí)行insert語(yǔ)句時(shí)會(huì)生成相關(guān)的delete語(yǔ)句的undo log。反之執(zhí)行delete語(yǔ)句也會(huì)生成相關(guān)的insert語(yǔ)句的undo log。執(zhí)行update語(yǔ)句時(shí)也是如此,不過(guò)update語(yǔ)句在執(zhí)行undo log回滾時(shí)有可能會(huì)涉及到MVCC。主要是為了保證在執(zhí)行undo log的時(shí)候的select能看到哪個(gè)版本的數(shù)據(jù)。

持久性(D)

持久性是指事務(wù)一旦提交,對(duì)數(shù)據(jù)庫(kù)的操作就是永久性的,接下來(lái)的其他操作和異常故障不應(yīng)該對(duì)它有任何影響。
我們都知道MySQL的數(shù)據(jù)最終是存放在磁盤中的,所以才會(huì)有磁盤的容量大小決定數(shù)據(jù)容量的大小。但是如果對(duì)MySQL的操作都是通過(guò)讀寫磁盤來(lái)進(jìn)行的話,那么光是磁盤的I/O就夠把效率大大的拉低了。

所以InnoDB為MySQL提供了緩沖池(Buffer Pool),Buffer Pool中包含了磁盤中部分?jǐn)?shù)據(jù)頁(yè)的映射。
當(dāng)從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)時(shí),會(huì)先從Buffer Pool中讀取數(shù)據(jù),如果Buffer Pool中沒(méi)有,則從磁盤讀取后放入到Buffer Pool中。
當(dāng)向數(shù)據(jù)庫(kù)寫入數(shù)據(jù)時(shí),會(huì)先寫入到Buffer Pool中,Buffer Pool中更新的數(shù)據(jù)會(huì)定期刷新到磁盤中(此過(guò)程稱為刷臟)。

雖然Buffer Pool為MySQL的讀寫提高了效率,但是卻也帶來(lái)了新的問(wèn)題,那就是如果數(shù)據(jù)剛更新到Buffer Pool中還沒(méi)來(lái)得及刷新到磁盤中時(shí),MySQL突然宕機(jī)了,這就會(huì)導(dǎo)致數(shù)據(jù)丟失,造成事務(wù)的持久性無(wú)法保證了。
為了解決這個(gè)緩存的一致性問(wèn)題,redo log就出現(xiàn)了。在對(duì)Buffer Pool中的數(shù)據(jù)進(jìn)行修改的時(shí)候通過(guò)redo log記錄這次操作,當(dāng)事務(wù)提交時(shí)會(huì)通過(guò)fsync接口對(duì)redo log進(jìn)行刷盤。

因?yàn)樵谑聞?wù)提交時(shí)會(huì)把redo log是同步在磁盤中的,所以當(dāng)MySQL出現(xiàn)宕機(jī)時(shí),可以從磁盤中讀取redo log進(jìn)行數(shù)據(jù)的恢復(fù),從而保證了事務(wù)的持久性。

redo log 采用的預(yù)寫的方式記錄日志,即先記錄日志,再更新Buffer Pool,這樣就強(qiáng)行的保證了,數(shù)據(jù)只要保存在了redo log中就一定會(huì)存儲(chǔ)到磁盤中了。

這要解釋一下,redo log 也是寫磁盤,刷臟也是寫磁盤,為啥要先記錄redo log而不是直接刷臟?

主要原因就是redo log比刷臟快很多。

第一點(diǎn)是,redo log是追加操作日志,是順序IO;而刷臟是隨機(jī)IO,因?yàn)槊看胃碌臄?shù)據(jù)不一定是挨著的,也就是隨機(jī)的。

第二點(diǎn)是,刷臟是以數(shù)據(jù)頁(yè)(Page)為單位的(即每次最少?gòu)拇疟P中讀取一頁(yè)數(shù)據(jù)到內(nèi)存,或者最少刷一頁(yè)數(shù)據(jù)到磁盤),MySQL默認(rèn)頁(yè)大小是16KB,對(duì)一個(gè)頁(yè)上的修改,都要整個(gè)頁(yè)都刷到磁盤中;而redo log只包含真正的需要寫入磁盤的操作日志。

MySQL還有一個(gè)記錄操作的日志,叫binlog ,那么redo log和binlog又有什么區(qū)別呢?

  • 第一點(diǎn)作用上的區(qū)別:

redo log是用來(lái)記錄更新緩存的,為了保證MySQL就算宕機(jī)也不會(huì)影響事務(wù)的持久性;binlog是用來(lái)記錄什么時(shí)間操作了什么,主要有時(shí)間點(diǎn),可以保證將數(shù)據(jù)恢復(fù)到某個(gè)時(shí)間點(diǎn),也有用于主從同步數(shù)據(jù)的。

  • 第二點(diǎn)層次上的區(qū)別:

redo log是存儲(chǔ)引擎InnoDB實(shí)現(xiàn)的(MyISAM就沒(méi)有redo log),而binlog是在MySQL服務(wù)器層面存在的任何其他存儲(chǔ)引擎也有binlog。
存儲(chǔ)內(nèi)容上,redo log是物理日志,基于磁盤的數(shù)據(jù)頁(yè),binlog是邏輯日志,存儲(chǔ)的一條執(zhí)行SQL。

  • 第三點(diǎn)寫入時(shí)機(jī)的區(qū)別:

redo log 在默認(rèn)情況下是在事務(wù)提交時(shí),進(jìn)行刷盤的;可以通過(guò)參數(shù):innodb_flush_log_at_trx_commit 來(lái)改變策略,可以不用等到事務(wù)提交時(shí)才進(jìn)行刷盤。
如:可以設(shè)置成每秒提交一次。
binlog是在事務(wù)提交時(shí)寫入。

隔離性(I)

原子性和持久性都是基于單個(gè)事務(wù)內(nèi)部的措施,而隔離性是只多個(gè)事務(wù)之間相互隔離,互不影響的特性。
我們都知道事務(wù)的隔離級(jí)別中最嚴(yán)謹(jǐn)?shù)氖谴谢⊿erializable),但是隔離性越高,性能就越低,所以一般不使用串行化這個(gè)隔離級(jí)別。
對(duì)于隔離性的,我們要分兩種情況進(jìn)行討論:

  • 一個(gè)事務(wù)中的寫操作對(duì)另一個(gè)事務(wù)中的寫操作的影響;
  • 一個(gè)事務(wù)中的寫操作對(duì)另一個(gè)事務(wù)中的讀操作的影響;

首先,事務(wù)間的寫操作其實(shí)是靠MySQL的鎖機(jī)制來(lái)實(shí)現(xiàn)隔離的,而事務(wù)間的寫和讀操作是靠MVCC機(jī)制來(lái)實(shí)現(xiàn)的。

鎖機(jī)制

MySQL中的鎖主要有

按照功能分:讀鎖和寫鎖;按照作用范圍分:表級(jí)鎖和行級(jí)鎖;
還有意向鎖,間隙鎖等。

讀鎖:又稱“共享鎖”,是指多個(gè)事務(wù)可以共享一把鎖,都只能訪問(wèn)數(shù)據(jù),并不能修改。

寫鎖:又稱“排他鎖”,是不能和其他事務(wù)共享數(shù)據(jù)的,如果一個(gè)事務(wù)獲取到了一個(gè)數(shù)據(jù)的排他鎖,那么其他事務(wù)就不能再獲取該行的其他鎖,包括共享鎖和排他鎖。

表級(jí)鎖:是指會(huì)將整個(gè)表進(jìn)行鎖定,性能較差,不同存儲(chǔ)引擎支持的鎖的粒度不同,MyISAM引擎支持表級(jí)鎖,InnoDB引擎支持表級(jí)鎖也支持行級(jí)鎖。

行級(jí)鎖:會(huì)將需要操作的相應(yīng)行進(jìn)行鎖定,性能好。

意向鎖:意向鎖是表級(jí)鎖,如果在一個(gè)事務(wù)已經(jīng)對(duì)一個(gè)表中的某個(gè)數(shù)據(jù)加上了排他鎖或共享鎖,那么就可以加上意向鎖,這樣當(dāng)下一個(gè)事務(wù)來(lái)進(jìn)行鎖表的時(shí)候發(fā)現(xiàn)已經(jīng)存在意向鎖了,就會(huì)先被阻塞,如果不加意向鎖的話,第二個(gè)事務(wù)來(lái)鎖表的時(shí)候需要一行一行的遍歷查看是否有數(shù)據(jù)已經(jīng)被鎖住了。

間隙鎖:間隙鎖是為了防止產(chǎn)生幻讀而加的鎖,加在不存在的空閑空間,可以是兩個(gè)索引記錄之間,也可能是第一個(gè)索引記錄之前或最后一個(gè)索引之后的空間(但是并不包含當(dāng)前記錄)。這樣就保證了在間隙鎖執(zhí)行的時(shí)候,新增的數(shù)據(jù)會(huì)阻塞,保證了一個(gè)事務(wù)中的兩次查詢獲得的記錄數(shù)都是一致的。

Next-Key Lock:Next-Key Lock是行級(jí)鎖和間隙鎖的結(jié)合產(chǎn)生的鎖,因?yàn)殚g隙鎖是不會(huì)鎖住當(dāng)前記錄的而Next-Key Lock是會(huì)將當(dāng)前記錄也鎖住的。

例如:如果一個(gè)表中有三條數(shù)據(jù)分別是:

id name number
1 小明 16
2 小紅 17
3 小張 20
4 小王 20

那么在執(zhí)行SQL:select * from table where number = 17 for update 時(shí)間隙鎖會(huì)鎖住,number的區(qū)間是(16,17),(17,20),但是Next-Key Lock的鎖住的是:
16,17),(17,20)區(qū)間加間隙鎖,同時(shí)number=17加記錄鎖。

鎖機(jī)制保障了多個(gè)事務(wù)間的寫操作的隔離,而多個(gè)事務(wù)間的讀和寫操作的保證是需要通過(guò)MVCC機(jī)制來(lái)保證的。

MVCC機(jī)制

MVCC全稱是【Multi-Version ConCurrency Control】即多版本控制協(xié)議。

MVCC的主要是靠在每行記錄上增加隱藏列和使用undo log來(lái)實(shí)現(xiàn)的,隱藏列主要包括,改行數(shù)據(jù)創(chuàng)建的版本號(hào)(遞增的),刪除時(shí)間,指向undo log的指針等。

那么MVCC是如何保證讀寫隔離的呢?主要是通過(guò)快照讀和當(dāng)前讀兩個(gè)操作。

  • 快照讀:

MVCC為了保證并發(fā)的效率,在進(jìn)行讀取數(shù)據(jù)的時(shí)候是不加鎖的,在執(zhí)行select的時(shí)候(不帶鎖的普通select),會(huì)先讀取當(dāng)前數(shù)據(jù)的版本號(hào),如果在select還沒(méi)返回結(jié)果時(shí),有事務(wù)將此行數(shù)據(jù)進(jìn)行了修改,那么版本號(hào)就會(huì)比執(zhí)行select的時(shí)候的大,所以為了保證select讀取數(shù)據(jù)的一致性,就只會(huì)讀取小于或等于當(dāng)前版本的數(shù)據(jù),這個(gè)歷史版本的數(shù)據(jù)就是從undo log中獲取到的。

  • 當(dāng)前讀:

當(dāng)執(zhí)行insert、update、delete的時(shí)候,是讀取的當(dāng)前最新的版本數(shù)據(jù),并且會(huì)給當(dāng)前記錄加上鎖,用來(lái)保證在操作的時(shí)候不會(huì)被別的事務(wù)將版本號(hào)進(jìn)行修改。

像普通的select就是快照讀即讀取的有可能就是數(shù)據(jù)的歷史版本。

insert、update、delete、select ... lock in share mode 和select ... for update 讀取的就是當(dāng)前讀,即讀取的都是數(shù)據(jù)的最新版本。

其實(shí)將隔離級(jí)別設(shè)置為Serializable也是可以實(shí)現(xiàn)讀寫隔離的,但是并發(fā)效率會(huì)比低很多,所以一般用的很少,但是MVCC是讀不加鎖的,只有在寫的時(shí)候才會(huì)加鎖,從而提高的并發(fā)的效率。

通過(guò)MVCC機(jī)制保證了多個(gè)事務(wù)間的讀寫隔離,從而實(shí)現(xiàn)了事務(wù)的隔離性。

一致性(C)

一致性是指在事務(wù)執(zhí)行前后,數(shù)據(jù)的一致性,事務(wù)前后數(shù)據(jù)完整性沒(méi)有破壞,并且都是合法的數(shù)據(jù)狀態(tài)。

  • 其中一致性的指標(biāo)有:

索引的完整(唯一索引,不重復(fù)等),數(shù)據(jù)列的完成(字段類型,長(zhǎng)度,大小符合要求),外鍵約束等。

  • 實(shí)現(xiàn)一致性的措施:

保證原子性,持久性,隔離性,如果這些特性都無(wú)法保證,那么一致性就也無(wú)法保證了。從數(shù)據(jù)庫(kù)層面來(lái)看,除了前面那幾個(gè)特性的保證外,對(duì)字段的一致性是有保證措施的,例如整型的字符不能傳入,字符串、時(shí)間等格式,字符串的長(zhǎng)度不能超過(guò)列的限制。但是在應(yīng)用層面也是需要開(kāi)發(fā)者自己來(lái)保證的,
例如:從A轉(zhuǎn)賬給B一部分金額,那么就要保證,從A從將金額扣除多少就要去給B增加多少金額,如果只扣除A的金額,而沒(méi)有增加B的金額,是無(wú)法保證一致性的。

另外,MySQL還通過(guò)兩階段提交事務(wù),保證了redo log和binlog之間的數(shù)據(jù)一致性問(wèn)題。

通過(guò)上面介紹持久性的時(shí)候解釋了,redo log和binlog的區(qū)別了,在區(qū)別中的第三條有說(shuō)到,在默認(rèn)情況下,事務(wù)提交時(shí),既寫redo log 有寫binlog那么他們是如何協(xié)調(diào)一致性的呢?事務(wù)提交成功以寫入哪個(gè)日志為準(zhǔn)呢?
MySQL通過(guò)兩階段提交來(lái)保證這兩個(gè)日志的數(shù)據(jù)一致性。

  • 第一階段提交,

將redo log提交到磁盤,并將狀態(tài)改為prepare狀態(tài),binlog不做任何操作。

  • 第二階段提交,

1、生成事務(wù)操作的binlog,并將binlog寫入到磁盤中。

2、調(diào)用引擎的提交事務(wù)接口,將redo log的狀態(tài)從prepare改為commit,事務(wù)提交完成。
通過(guò)上面這兩階段提交保證了事務(wù)數(shù)據(jù)的一致性。
當(dāng)事務(wù)提交時(shí)redo log處于prepare階段時(shí),發(fā)生MySQL宕機(jī)或崩潰,則會(huì)執(zhí)行事務(wù)回滾。
當(dāng)事務(wù)提交redo log處于commit階段時(shí),發(fā)生了崩潰會(huì)執(zhí)行事務(wù)恢復(fù),本機(jī)事務(wù)通過(guò)redol og進(jìn)行恢復(fù),而如果是主從數(shù)據(jù)庫(kù)的話,在commit階段,會(huì)根據(jù)binlog對(duì)從庫(kù)進(jìn)行數(shù)據(jù)恢復(fù)。
這就是以寫入binlog成功為提交事務(wù)成功的依據(jù)。因?yàn)橐话阍诒罎⒒謴?fù)的時(shí)候都是用binlong進(jìn)行恢復(fù)的,如果還未生成binlog,只寫入了redo log。在恢復(fù)的時(shí)候redo log恢復(fù)的是一個(gè)版本的數(shù)據(jù),而通過(guò)bin log恢復(fù)的從庫(kù)數(shù)據(jù)會(huì)是之前的一個(gè)時(shí)間點(diǎn)的binlog版本的數(shù)據(jù),這樣數(shù)據(jù)就導(dǎo)致不一致了。

總結(jié)

MySQL事務(wù)的ACID,一致性是最終目的。
保證一致性的措施有:

  • A原子性:靠undo log來(lái)保證(異?;驁?zhí)行失敗后進(jìn)行回滾)。
  • D持久性:靠redo log來(lái)保證(保證當(dāng)MySQL宕機(jī)或停電后,可以通過(guò)redo log最終將數(shù)據(jù)保存至磁盤中)。
  • I隔離性:事務(wù)間的讀寫靠MySQL的鎖機(jī)制來(lái)保證隔離,事務(wù)間的寫操作靠MVCC機(jī)制(快照讀、當(dāng)前讀)來(lái)保證隔離性。
  • C一致性:事務(wù)的最終目的,即需要數(shù)據(jù)庫(kù)層面保證,又需要應(yīng)用層面進(jìn)行保證,并且MySQL底層通過(guò)兩階段提交事務(wù)保證了事務(wù)持久化時(shí)的一致性。

以上就是MySQL如何實(shí)現(xiàn)事務(wù)的ACID的詳細(xì)內(nèi)容,更多關(guān)于MySQL實(shí)現(xiàn)事務(wù)的ACID的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

您可能感興趣的文章:
  • Mysql中事務(wù)ACID的實(shí)現(xiàn)原理詳解
  • MySQL令人大跌眼鏡的隱式轉(zhuǎn)換
  • MySQL非空約束(not null)案例講解
  • 解決mysql數(shù)據(jù)庫(kù)數(shù)據(jù)遷移達(dá)夢(mèng)數(shù)據(jù)亂碼問(wèn)題
  • MySQL連接異常報(bào)10061錯(cuò)誤問(wèn)題解決
  • MySQL事務(wù)控制流與ACID特性

標(biāo)簽:阜新 合肥 貴州 臺(tái)灣 鎮(zhèn)江 北京 鷹潭 日照

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《MySQL如何實(shí)現(xiàn)事務(wù)的ACID》,本文關(guān)鍵詞  MySQL,如何,實(shí)現(xiàn),事務(wù),的,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《MySQL如何實(shí)現(xiàn)事務(wù)的ACID》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于MySQL如何實(shí)現(xiàn)事務(wù)的ACID的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章