<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>

          3000幀動畫圖解MySQL為什么需要binlog、redo log和undo log

          共 5888字,需瀏覽 12分鐘

           ·

          2022-06-20 22:17

          全文建立在MySQL的存儲引擎為InnoDB的基礎上

          Hollis的新書限時折扣中,一本深入講解Java基礎的干貨筆記!

          先看一條SQL如何入庫的:

          這是一條很簡單的更新SQL,從MySQL服務端接收到SQL到落盤,先后經(jīng)過了MySQL Server層和InnoDB存儲引擎。

          1. Server層就像一個產(chǎn)品經(jīng)理,分析客戶的需求,并給出實現(xiàn)需求的方案。

          2. InnoDB就像一個基層程序員,實現(xiàn)產(chǎn)品經(jīng)理給出的具體方案。

          在MySQL”分析需求,實現(xiàn)方案“的過程中,還夾雜著內存操作和磁盤操作,以及記錄各種日志。

          他們到底有什么用處?他們之間到底怎么配合的?MySQL又為什么要分層呢?InnoDB里面的那一塊Buffer Pool又是什么?

          我們慢慢分析。

          分層結構

          MySQL為什么要分為Server層和存儲引擎兩層呢?

          這個問題官方也沒有給出明確的答案,但是也不難猜,簡單來說就是為了“解耦”。

          Server層和存儲引擎各司其職,分工明確,用戶可以根據(jù)不同的需求去使用合適的存儲引擎,多好的設計,對不對?

          后來的發(fā)展也驗證了“分層設計”的優(yōu)越性:

          MySQL最初搭載的存儲引擎是自研的只支持簡單查詢的MyISAM的前身ISAM,后來與Sleepycat合作研發(fā)了Berkeley DB引擎,支持了事務。

          江山代有才人出,技術后浪推前浪,MySQL在持續(xù)的升級著自己的存儲引擎的過程中,遇到了橫空出世的InnoDB,InnoDB的功能強大讓MySQL倍感壓力。

          自己的存儲引擎打不過InnoDB怎么辦?

          打不過就加入!

          MySQL選擇了和InnoDB合作。正是因為MySQL存儲引擎的插件化設計,兩個公司合作的非常順利,MySQL也在合作后不久就發(fā)布了正式支持nnoDB的4.0版本以及經(jīng)典的4.1版本。

          MySQL兼并天下模式也成為MySQL走向繁榮的一個重要因素。這能讓MySQL長久地保持著極強競爭力。

          時至今日,MySQL依然占據(jù)著極高數(shù)據(jù)庫市場份額,僅次于王牌數(shù)據(jù)庫Oracle。

          Buffer Pool

          在InnoDB里,有一塊非常重要的結構——Buffer Pool。

          Buffer Pool是個什么東西呢?

          Buffer Pool就是一塊用于緩存MySQL磁盤數(shù)據(jù)的內存空間。

          為什么要緩存MySQL磁盤數(shù)據(jù)呢?

          我們通過一個例子說明,我們先假設沒有Buffer Pool,user表里面只有一條記錄,記錄的age = 1,假設需要執(zhí)行三條SQL:

          1. 事務A:update user set age = 2
          2. 事務B:update user set age = 3
          3. 事務C:update user set age = 4

          如果沒有Buffer Pool,那執(zhí)行就是這樣的:

          從圖上可以看出,每次更新都需要從磁盤拿數(shù)據(jù)(1次IO),修改完了需要刷到磁盤(1次IO),也就是每次更新都需要2次磁盤IO。三次更新需要6次磁盤IO。

          而有了Buffer Pool,執(zhí)行就成了這樣:

          從圖上可以看出,只需要在第一次執(zhí)行的時候將數(shù)據(jù)從磁盤拿到Buffer Pool(1次IO),第三次執(zhí)行完將數(shù)據(jù)刷回磁盤(1次IO),整個過程只需要2次磁盤IO,比沒有Buffer Pool節(jié)省了4次磁盤IO的時間。

          當然,Buffer Pool真正的運轉流程沒有這么簡單,具體實現(xiàn)細節(jié)和優(yōu)化技巧還有很多,由于篇幅有限,本文不做詳細描述。

          我想表達的是:Buffer Pool就是將磁盤IO轉換成了內存操作,節(jié)省了時間,提高了效率。

          Buffer Pool是提高了效率沒錯,但是出現(xiàn)了一個問題,Buffer Pool是基于內存的,而只要一斷電,內存里面的數(shù)據(jù)就會全部丟失。

          如果斷電的時候Buffer Pool的數(shù)據(jù)還沒來得及刷到磁盤,那么這些數(shù)據(jù)不丟失了嗎?

          還是上面的那個例子,如果三個事務執(zhí)行完畢,在age = 4還沒有刷到磁盤的時候,突然斷電,數(shù)據(jù)就全部丟掉了:

          試想一下,如果這些丟失的數(shù)據(jù)是核心的用戶交易數(shù)據(jù),那用戶能接受嗎?

          答案是否定的。

          那InnoDB是如何做到數(shù)據(jù)不會丟失的呢?

          今天的第一個日志——redo log登場了。

          恢復 - redo log

          顧名思義,redo是重做的意思,redo log就是重做日志的意思。

          redo log是如何保證數(shù)據(jù)不會丟失的呢?

          就是在修改之后,先將修改后的值記錄到磁盤上的redo log中,就算突然斷電了,Buffer Pool中的數(shù)據(jù)全部丟失了,來電的時候也可以根據(jù)redo log恢復Buffer Pool,這樣既利用到了Buffer Pool的內存高效性,也保證了數(shù)據(jù)不會丟失。

          我們通過一個例子說明,我們先假設沒有Buffer Pool,user表里面只有一條記錄,記錄的age = 1,假設需要執(zhí)行一條SQL:

          1. 事務A:update user set age = 2

          執(zhí)行過程如下:

          如上圖,有了redo log之后,將age修改成2之后,馬上將age = 2寫到redo log里面,如果這個時候突然斷電內存數(shù)據(jù)丟失,在來電的時候,可以將redo log里面的數(shù)據(jù)讀出來恢復數(shù)據(jù),用這樣的方式保證了數(shù)據(jù)不會丟失。

          你可能會問,redo log文件也在磁盤上,數(shù)據(jù)文件也在磁盤上,都是磁盤操作,何必多一舉?為什么不直接將修改的數(shù)據(jù)寫到數(shù)據(jù)文件里面去呢?

          傻瓜,因為redo log是磁盤順序寫,數(shù)據(jù)刷盤是磁盤隨機寫,磁盤的順序寫比隨機寫高效的多啊。

          這種先預寫日志后面再將數(shù)據(jù)刷盤的機制,有一個高大上的專業(yè)名詞——WAL(Write-ahead logging),翻譯成中文就是預寫式日志。

          雖然磁盤順序寫已經(jīng)很高效了,但是和內存操作還是有一定的差距。

          那么,有沒有辦法進一步優(yōu)化一下呢?

          答案是可以。那就是給redo log也加一個內存buffer,也就是redo log buffer,用這種套娃式的方法進一步提高效率。

          redo log buffer具體是怎么配合刷盤呢?

          回答這個問題之前之前,我們先來捋一下MySQL服務端和操作系統(tǒng)的關系:

          MySQL服務端是一個進程,它運行于操作系統(tǒng)之上。也就是說,操作系統(tǒng)掛了MySQL一定掛了,但是MySQL掛了操作系統(tǒng)不一定掛。

          所以MySQL掛了有兩種情況:

          1. MySQL掛了,操作系統(tǒng)也掛了,也就是常說的服務器宕機了。這種情況Buffer Pool里面的數(shù)據(jù)會全部丟失,操作系統(tǒng)的os cache里面的數(shù)據(jù)也會丟失。
          2. MySQL掛了,操作系統(tǒng)沒有掛。這種情況Buffer Pool里面的數(shù)據(jù)會全部丟失,操作系統(tǒng)的os cache里面的數(shù)據(jù)不會丟失。

          OK,了解了MySQL服務端和操作系統(tǒng)的關系之后,再來看redo log的落盤機制。redo log的刷盤機制由參數(shù)innodb_flush_log_at_trx_commit控制,這個參數(shù)有3個值可以設置:

          1. innodb_flush_log_at_trx_commit = 1:實時寫,實時刷
          2. innodb_flush_log_at_trx_commit = 0:延遲寫,延遲刷
          3. innodb_flush_log_at_trx_commit = 2:實時寫,延遲刷

          寫可以理解成寫到操作系統(tǒng)的緩存(os cache),刷可以理解成把操作系統(tǒng)里面的緩存刷到磁盤。

          這三種策略的區(qū)別,我們分開討論:

          innodb_flush_log_at_trx_commit = 1:實時寫,實時刷

          這種策略會在每次事務提交之前,每次都會將數(shù)據(jù)從redo log刷到磁盤中去,理論上只要磁盤不出問題,數(shù)據(jù)就不會丟失。

          總結來說,這種策略效率最低,但是丟數(shù)據(jù)風險也最低。

          innodb_flush_log_at_trx_commit = 0:延遲寫,延遲刷

          這種策略在事務提交時,只會把數(shù)據(jù)寫到redo log buffer中,然后讓后臺線程定時去將redo log buffer里面的數(shù)據(jù)刷到磁盤。

          這種策略是最高效的,但是我們都知道,定時任務是有間隙的,但是如果事務提交后,后臺線程沒來得及將redo log刷到磁盤,這個時候不管是MySQL進程掛了還是操作系統(tǒng)掛了,這一部分數(shù)據(jù)都會丟失。

          總結來說這種策略效率最高,丟數(shù)據(jù)的風險也最高。

          innodb_flush_log_at_trx_commit = 2:實時寫,延遲刷

          這種策略在事務提交之前會把redo log寫到os cache中,但并不會實時地將redo log刷到磁盤,而是會每秒執(zhí)行一次刷新磁盤操作。

          這種情況下如果MySQL進程掛了,操作系統(tǒng)沒掛的話,操作系統(tǒng)還是會將os cache刷到磁盤,數(shù)據(jù)不會丟失,如下圖:

          但如果MySQL所在的服務器掛掉了,也就是操作系統(tǒng)都掛了,那么os cache也會被清空,數(shù)據(jù)還是會丟失。如下圖:

          所以,這種redo log刷盤策略是上面兩種策略的折中策略,效率比較高,丟失數(shù)據(jù)的風險比較低,絕大多情況下都推薦這種策略。

          總結一下,redo log的作用是用于恢復數(shù)據(jù),寫redo log的過程是磁盤順序寫,有三種刷盤策略,有innodb_flush_log_at_trx_commit 參數(shù)控制,推薦設置成2。

          回滾 - undo log

          我們都知道,InnoDB是支持事務的,而事務是可以回滾的。

          假如一個事務將age=1修改成了age=2,在事務還沒有提交的時候,后臺線程已經(jīng)將age=2刷入了磁盤。這個時候,不管是內存還是磁盤上,age都變成了2,如果事務要回滾,找不到修改之前的age=1,無法回滾了。

          那怎么辦呢?

          很簡單,把修改之前的age=1存起來,回滾的時候根據(jù)存起來的age=1回滾就行了。

          MySQL確實是這么干的!這個記錄修改之前的數(shù)據(jù)的過程,叫做記錄undo log。undo翻譯成中文是撤銷、回滾的意思,undo log的主要作用也就是回滾數(shù)據(jù)。

          如何回滾呢?看下面這個圖:

          MySQL在將age = 1修改成age = 2之前,先將age = 1存到undo log里面去,這樣需要回滾的時候,可以將undo log里面的age = 1讀出來回滾。

          需要注意的是,undo log默認存在全局表空間里面,你可以簡單的理解成undo log也是記錄在一個MySQL的表里面,插入一條undo log和插入一條普通數(shù)據(jù)是類似。也就是說,寫undo log的過程中同樣也是要寫入redo log的。

          歸檔 - binlog

          undo log記錄的是修改之前的數(shù)據(jù),提供回滾的能力。

          redo log記錄的是修改之后的數(shù)據(jù),提供了崩潰恢復的能力。

          那binlog是干什么的呢?

          binlog記錄的是修改之后的數(shù)據(jù),用于歸檔。

          和redo log日志類似,binlog也有著自己的刷盤策略,通過sync_binlog參數(shù)控制:

          • sync_binlog = 0 :每次提交事務前將binlog寫入os cache,由操作系統(tǒng)控制什么時候刷到磁盤
          • sync_binlog =1 :采用同步寫磁盤的方式來寫binlog,不使用os cache來寫binlog
          • sync_binlog = N :當每進行n次事務提交之后,調用一次fsync將os cache中的binlog強制刷到磁盤

          那么問題來了,binlog和redo log都是記錄的修改之后的值,這兩者有什么區(qū)別呢?有redo log為什么還需要binlog呢?

          首先看兩者的一些區(qū)別:

          • binlog是邏輯日志,記錄的是對哪一個表的哪一行做了什么修改;redo log是物理日志,記錄的是對哪個數(shù)據(jù)頁中的哪個記錄做了什么修改,如果你還不了解數(shù)據(jù)頁,你可以理解成對磁盤上的哪個數(shù)據(jù)做了修改。
          • binlog是追加寫;redo log是循環(huán)寫,日志文件有固定大小,會覆蓋之前的數(shù)據(jù)。
          • binlog是Server層的日志;redo log是InnoDB的日志。如果不使用InnoDB引擎,是沒有redo log的。

          但說實話,我覺得這些區(qū)別并不是redo log不能取代binlog的原因,MySQL官方完全可以調整redo log讓他兼并binlog的能力,但他沒有這么做,為什么呢?

          我認為不用redo log取代binlog最大的原因是“沒必要”。

          為什么這么說呢?

          第一點,binlog的生態(tài)已經(jīng)建立起來。MySQL高可用主要就是依賴binlog復制,還有很多公司的數(shù)據(jù)分析系統(tǒng)和數(shù)據(jù)處理系統(tǒng),也都是依賴的binlog。取代binlog去改變一個生態(tài)費力了不討好。

          第二點,binlog并不是MySQL的瓶頸,花時間在沒有瓶頸的地方?jīng)]必要。

          總結

          1. Buffer Pool是MySQL進程管理的一塊內存空間,有減少磁盤IO次數(shù)的作用。
          2. redo log是InnoDB存儲引擎的一種日志,主要作用是崩潰恢復,有三種刷盤策略,有innodb_flush_log_at_trx_commit 參數(shù)控制,推薦設置成2。
          3. undo log是InnoDB存儲引擎的一種日志,主要作用是回滾。
          4. binlog是MySQL Server層的一種日志,主要作用是歸檔。
          5. MySQL掛了有兩種情況:操作系統(tǒng)掛了MySQL進程跟著掛了;操作系統(tǒng)沒掛,但是MySQL進程掛了。

          最后,再用一張圖總結一下全文的知識點:

          寫在最后

          這篇文章寫在一年之前,本來覺得是一篇水文沒想要發(fā),最近無聊修改了一下發(fā)了出來,希望能夠用動圖的形式幫助到MySQL基礎不太好的朋友,大神忽略就好。

          需要強調的一點是,由于作者水平有限,本文只是淺顯的從無到有地闡述了MySQL幾種日志的大致作用,過程中省略了很多細節(jié),比如Buffer Pool的實現(xiàn)細節(jié),比如undo log和MVCC的關系,比如binlog buffer、change buffer的存在,比如redo log的兩階段提交。

          如果您有任何問題,我們可以探討,如果您在文中發(fā)現(xiàn)錯誤,還望您指出,萬分感謝!

          參考資料

          • 《MySQL實戰(zhàn)45講》
          • 《從根兒上理解MySQL》
          • 《MySQL技術內幕—InnoDB存儲引擎》第2版



          我的新書《深入理解Java核心技術》已經(jīng)上市了,上市后一直蟬聯(lián)京東暢銷榜中,目前正在6折優(yōu)惠中,想要入手的朋友千萬不要錯過哦~長按二維碼即可購買~


          長按掃碼享受6折優(yōu)惠


          往期推薦

          還在用 SimpleDateFormat 做時間格式化?小心項目崩掉!


          Logback 配置文件這樣優(yōu)化,TPS提高 10 倍


          入職一家新公司,如何快速熟悉代碼?



          有道無術,術可成;有術無道,止于術

          歡迎大家關注Java之道公眾號


          好文章,我在看??

          瀏覽 60
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  青青草超碰免费 | 99精品观看 | 久久久91精品国产一区陈可心 | 欧美一级片在线观看 | 五月天狠狠干 |