事物与持久化_跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现...
提到MySQL的事物,我相信對(duì)MySQL有了解的同學(xué)都能聊上幾句,無(wú)論是面試求職,還是日常開(kāi)發(fā),MySQL的事務(wù)都跟我們息息相關(guān)。
而事務(wù)的ACID(即原子性Atomicity、一致性Consistency、隔離性Isolation、持久性Durability)可以說(shuō)涵蓋了事務(wù)的全部知識(shí)點(diǎn),所以,我們不僅要知道ACID是什么,還要了解ACID背后的實(shí)現(xiàn),只有這樣,無(wú)論在日常開(kāi)發(fā)還是面試求職,都能無(wú)往而不利。
上一篇 跟面試官侃半小時(shí)MySQL事務(wù)隔離性,從基本概念深入到實(shí)現(xiàn) 主要圍繞“隔離性”展開(kāi),從基本概念,到隔離性的實(shí)現(xiàn),最后以一個(gè)實(shí)戰(zhàn)案例進(jìn)行融會(huì)貫通。本篇內(nèi)容將介紹原子性、一致性、持久性相關(guān)實(shí)現(xiàn),由于這部分內(nèi)容可能很多人會(huì)相對(duì)陌生,因?yàn)槿粘I(yè)務(wù)開(kāi)發(fā)可能不太會(huì)去接觸和深究,但是了解完后,你對(duì)MySQL會(huì)有更深刻的認(rèn)識(shí)。
1.基本概念
- 原子性。
整個(gè)事務(wù)是不可分割的最小單位,事務(wù)中任何一個(gè)語(yǔ)句執(zhí)行失敗,所有已經(jīng)執(zhí)行成功的語(yǔ)句也要會(huì)滾,整個(gè)數(shù)據(jù)庫(kù)狀態(tài)要恢復(fù)到執(zhí)行事務(wù)前到狀態(tài)。
- 一致性。
事務(wù)將數(shù)據(jù)庫(kù)從一種狀態(tài)轉(zhuǎn)變?yōu)橄乱环N一致的狀態(tài)。在事務(wù)的前后,數(shù)據(jù)庫(kù)的完整性約束沒(méi)有被破壞。(事務(wù)的acid不是完全正交的,尤其是一致性,可能跟原子性、隔離性都有一定關(guān)系,后面會(huì)看到)
- 持久性。
事務(wù)一旦提交,那么就是永久性的,不會(huì)因?yàn)殄礄C(jī)等故障導(dǎo)致數(shù)據(jù)丟失(外力影響不保證,比如磁盤(pán)損害)。持久性是保證了數(shù)據(jù)庫(kù)的高可靠性(High Reliability),而不是高可用性(Hign Availability)。高可用性并不能通過(guò)事務(wù)來(lái)保證。
2.持久性的實(shí)現(xiàn)
MySQL的innoDB存儲(chǔ)引擎,使用Redo log保證了事務(wù)的持久性。
當(dāng)事務(wù)提交時(shí),必須先將事務(wù)的所有日志寫(xiě)入日志文件進(jìn)行持久化,就是我們常說(shuō)的WAL(write ahead log)機(jī)制(這個(gè)技術(shù)是保障持久性的關(guān)鍵技術(shù),在HBase中也扮演重要角色,有興趣的同學(xué)可以參考xxxxx)。這樣才能保證斷電或宕機(jī)等情況發(fā)生后,已提交的事務(wù)不會(huì)丟失,這個(gè)能力稱(chēng)為 crash-safe。
下面深入聊一聊redo log的機(jī)制,給大家更深刻的理解。
Redo log包括兩部分,重做日志緩沖(redo log buffer)和重做日志文件(redo log file),前者是易失的緩存,后者是持久化的文件。
舉一個(gè)事務(wù)的例子:
- 步驟1:begin;
- 步驟2:insert into t1 …r
- 步驟3:insert into t2 …
- 步驟4:commit;
這個(gè)事務(wù)的寫(xiě)入過(guò)程實(shí)際拆解如下:
innodb緩沖池的概念本文就不展開(kāi)說(shuō)明了,以后有機(jī)會(huì)可以展開(kāi)說(shuō)一下。
重點(diǎn)關(guān)注在這個(gè)事務(wù)提交前,將 redo log 的寫(xiě)入拆成了兩個(gè)步驟,prepare 和 commit,這就是"兩階段提交”。
為什么要采用兩階段提交呢?
實(shí)際上,兩階段提交是分布式系統(tǒng)常用的機(jī)制。MySQL使用了兩階段提交后,也是為了保證事務(wù)的持久性。Redo log 和bingo 有一個(gè)共同的數(shù)據(jù)字段,叫 XID,崩潰恢復(fù)的時(shí)候,會(huì)按順序掃描 redo log。
- 假設(shè)在寫(xiě)入binlog前系統(tǒng)崩潰,那么數(shù)據(jù)庫(kù)恢復(fù)后順序掃描 redo log,碰到只有 parepare、而沒(méi)有 commit 的 redo log,就拿著 XID 去 binlog 找對(duì)應(yīng)的事務(wù),而且binlog也沒(méi)寫(xiě)入,所以事務(wù)就直接回滾了。
- 假設(shè)在寫(xiě)入binlog之后,事務(wù)提交前數(shù)據(jù)庫(kù)崩潰,那么數(shù)據(jù)庫(kù)恢復(fù)后順序掃描 redo log,碰到既有 prepare、又有 commit 的 redo log,就直接提交,保證數(shù)據(jù)不丟失。
這個(gè)事務(wù)要往兩個(gè)表中插入記錄,插入數(shù)據(jù)的過(guò)程中,生成的日志都得先寫(xiě)入redo log buffer ,等到commit的時(shí)候,才真正把日志寫(xiě)到 redo log 文件。(當(dāng)然,這里不絕對(duì),因?yàn)閞edo log buffer可能因?yàn)槠渌虮黄人⑿碌絩edo log)。
而為了確保每次日志都能寫(xiě)入日志文件,在每次將重做日志緩沖 寫(xiě)入 重做日志文件 后,InnoDB存儲(chǔ)引擎都需要調(diào)用一次fsync操作,確保寫(xiě)入了磁盤(pán)。
對(duì)于redo log的持久化,可以如下圖所示。
1)先寫(xiě)入redo log buffer,在藍(lán)色區(qū)域。
2)寫(xiě)入redo log file,但是還沒(méi)有fsync,這時(shí)候是處于黃色的位置,處于系統(tǒng)緩存。
3)調(diào)用fsync,真正寫(xiě)入磁盤(pán)。
為了控制 redo log 的寫(xiě)入策略,InnoDB 提供了 innodb_flush_log_at_trx_commit 參數(shù),它有三種可能取值:
- 設(shè)置為 0 的時(shí)候,表示每次事務(wù)提交時(shí)都只是把 redo log 留在 redo log buffer 中 ;
- 設(shè)置為 1 的時(shí)候,表示每次事務(wù)提交時(shí)都將 redo log 直接持久化到磁盤(pán);
- 設(shè)置為 2 的時(shí)候,表示每次事務(wù)提交時(shí)都只是把 redo log 寫(xiě)到 page cache。
binlog的寫(xiě)入和redo log一樣,也是包括bingo cache和bingo file,同樣跟上面的三色層次類(lèi)似(當(dāng)然,binlog是server層的,不是存儲(chǔ)引擎層的),包括log buffer、文件系統(tǒng)page cache、hard disk。
寫(xiě)入page cache 和 fsync到disk 的時(shí)機(jī),是由參數(shù) sync_binlog 控制的:
- sync_binlog=0 的時(shí)候,表示每次提交事務(wù)都只 寫(xiě)入文件系統(tǒng)的page cache,不 fsync;
- sync_binlog=1 的時(shí)候,表示每次提交事務(wù)都會(huì)執(zhí)行 fsync;
- sync_binlog=N(N>1) 的時(shí)候,表示每次提交事務(wù)都寫(xiě)入文件系統(tǒng)的page cache,但累積 N 個(gè)事務(wù)后才 fsync。(如果主機(jī)發(fā)生異常重啟,會(huì)丟失最近 N 個(gè)事務(wù)的 binlog 日志)
通常我們說(shuō) MySQL 的“雙 1”配置,指的就是 sync_binlog 和 innodb_flush_log_at_trx_commit 都設(shè)置成 1。也就是說(shuō),一個(gè)事務(wù)完整提交前,需要等待兩次刷盤(pán),一次是 redo log(prepare 階段),一次是 binlog。
特別需要區(qū)分的是,redo log和binlog的不同。這也是經(jīng)常在面試中可能會(huì)問(wèn)到的兩種日志的差異。
注意有這么幾點(diǎn):
- 產(chǎn)生位置不同。
redo log是innodb的存儲(chǔ)引擎產(chǎn)生的,而binlog是數(shù)據(jù)庫(kù)的server層實(shí)現(xiàn)的。換句話(huà)說(shuō),如果你使用MySQL,換其他存儲(chǔ)引擎,那么可能沒(méi)有redo log,但是還是會(huì)有binlog。
- 日志記錄的內(nèi)容形式不同。
binlog是一種邏輯日志,記錄對(duì)應(yīng)的SQL語(yǔ)句,而redo log記錄了物理日志,是針對(duì)每個(gè)數(shù)據(jù)頁(yè)的修改。
- 日志寫(xiě)入磁盤(pán)時(shí)間不同。
binlog只有在事務(wù)提交后完成一次寫(xiě)入,對(duì)于一個(gè)事物而言,在binlog中只有一條記錄。而redo log在事務(wù)進(jìn)行中不斷被寫(xiě)入,而且是并發(fā)寫(xiě)入的,不是順序?qū)懭氲摹?/p>
- 保存方式不同。
redo log 是循環(huán)寫(xiě)的,空間固定會(huì)用完;binlog 是可以追加寫(xiě)入的。“追加寫(xiě)”是指 binlog 文件寫(xiě)到一定大小后會(huì)切換到下一個(gè),并不會(huì)覆蓋以前的日志。
3.原子性的實(shí)現(xiàn)
Undo log保證了事務(wù)的原子性。
在對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改時(shí),innoDB引擎除了會(huì)產(chǎn)生redo log,還會(huì)產(chǎn)生undo log。InnoDB實(shí)現(xiàn)回滾,靠的是undo log:當(dāng)事務(wù)對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改時(shí),InnoDB會(huì)生成對(duì)應(yīng)的undo log;如果事務(wù)執(zhí)行失敗導(dǎo)致事務(wù)需要回滾,就利用undo log中的信息將數(shù)據(jù)回滾到修改之前的樣子。
有人認(rèn)為undo log是redo log的逆過(guò)程,其實(shí)是不對(duì)的。兩個(gè)日志文件其實(shí)都能看作是一種對(duì)數(shù)據(jù)的恢復(fù)操作,redo log恢復(fù)事務(wù)導(dǎo)致的數(shù)據(jù)頁(yè)的修改,而undo log能夠恢復(fù)數(shù)據(jù)記錄到某個(gè)特定的版本。
所以redo log是一種物理日志(數(shù)據(jù)頁(yè)的修改),而undo log是一種邏輯日志(數(shù)據(jù)記錄)。
undo log還要另外一個(gè)重要作用,就是用于mvcc中,進(jìn)行多版本控制,也就是實(shí)現(xiàn)事務(wù)隔離性的基礎(chǔ),當(dāng)用戶(hù)讀取一行記錄時(shí),如果這個(gè)記錄已接被其他事務(wù)占用,那么當(dāng)前事務(wù)就可以通過(guò)undo讀取之前的行版本信息,用來(lái)實(shí)現(xiàn)非鎖定讀取,就是“快照讀”。(事務(wù)隔離性的問(wèn)題,可以看我上一篇文章 跟面試官侃半小時(shí)MySQL事務(wù)隔離性,從基本概念深入到實(shí)現(xiàn) )。
4.一致性的實(shí)現(xiàn)
就像一開(kāi)始在定義的時(shí)候介紹的,事務(wù)的ACID性質(zhì)不是完全正交的,尤其是一致性,我們可以認(rèn)為原子性、持久性和隔離性都是為了實(shí)現(xiàn)事務(wù)的一致性。
當(dāng)然,這里的一致性是指數(shù)據(jù)庫(kù)層面的事務(wù)一致性。
如果說(shuō)你在應(yīng)用層面做一個(gè)操作,給轉(zhuǎn)賬者扣錢(qián),沒(méi)給接收者加錢(qián),那么這個(gè)不一致跟事務(wù)的不一致是沒(méi)有關(guān)系的,需要開(kāi)發(fā)人員自己做業(yè)務(wù)邏輯一致性的保證。
這篇文章很難寫(xiě),一些知識(shí)可能不是特別常用,不說(shuō)可能會(huì)有疑惑,但是細(xì)扣又容易陷入細(xì)節(jié),前后反復(fù)修改了好幾遍,希望能邏輯連貫、深入淺出、杜絕又臭又長(zhǎng)。覺(jué)得不錯(cuò)的話(huà)點(diǎn)個(gè)關(guān)注、轉(zhuǎn)發(fā)一下吧。
希望能得到您的 關(guān)注、評(píng)論、轉(zhuǎn)發(fā),謝謝!
私信我“資料”,可以免費(fèi)獲取海量 JAVA技術(shù)棧電子書(shū) 和 大廠(chǎng)面試題。
總結(jié)
以上是生活随笔為你收集整理的事物与持久化_跟面试官侃半小时MySQL事务,说完原子性、一致性、持久性的实现...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: mplayer-php,mplayer+
- 下一篇: php-fpm 启动拥有者,php-fp