<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的redo log和bin log 真香

          共 5633字,需瀏覽 12分鐘

           ·

          2021-09-29 21:13

          大家好,我是連邊,這是我的第22篇原創(chuàng)文章。

          今天這篇文章給大家?guī)鞰ySQL中重要的兩個日志 - redo log、binlog,從理論概念出發(fā),結合圖解分析,看完這篇文章之后,你能對redo logbinlog有深入的理解。

          文章導讀

          MySQL兩個日志

          淺談MySQL分層架構

          在講具體的日志之前,先稍微鋪墊下MySQL分層的架構,讓大家知道redo logbinlog是由MySQL的哪一層產(chǎn)生的。

          MySQL分層架構圖

          MySQL整體分為3層:客戶端層,Server層和存儲引擎層。我們的binlog日志,由Server層生成,redo log是InnoDB特有的日志,由InnoDB引擎生成。

          重做日志(redo log)

          什么是redo log

          InnoDB為了能夠支持事務一系列操作,而事務有4種特性:原子性、一致性、隔離性持久性,在事務操作中,要么全部執(zhí)行,要么全部不執(zhí)行,這就是事務的目的。而我們的redo log用來保證事務的持久性,即我們常說的ACID中的D。我們只需要知道它是通過一套什么樣的機制,來保證持久性,就能掌握好redo log。

          這里的說的持久性,是說最后落盤到redo log文件(即常見的ib_logfile文件),因為最后我們異常情況的恢復,都是根據(jù)文件來做恢復的。

          那么我們的MySQL InnoDB是通過一套什么樣的機制來確保速度可靠性的呢?

          WAL

          在計算機體系中,CPU處理速度和硬盤的速度,是不在同一個數(shù)量級上的,為了讓它們速度匹配,從而催生了我們的內(nèi)存模塊,但是內(nèi)存有一個特點,就是掉電之后,數(shù)據(jù)就會丟失,不是持久的,我們需要持久化的數(shù)據(jù),最后都需要存儲到硬盤上。

          InnoDB引擎設計者也利用了類似的設計思想,先寫內(nèi)存,再寫硬盤,這樣就不會因為redo log寫硬盤IO而導致數(shù)據(jù)庫性能問題。在InnoDB中,這種技術有一個專業(yè)名稱,叫做Write-Ahead Log(預先日志持久化)

          先寫buffer 再寫磁盤

          redo log寫入策略

          上邊是保證了處理的速度,但是怎么樣保證寫入到硬盤的可靠性呢?

          InnoDB引擎的設計者也設計了一種寫入的策略,首先有一個后臺線程,每隔1秒,就會把redo log buffer中的日志,調用write寫到文件系統(tǒng)的page cache,然后調用fsync持久化到磁盤(即redo log文件 ib_logfile0 ib_logfile1)。

          為了控制 redo log寫入策略,InnoDB提供了innodb_flush_log_at_trx_commit配置參數(shù),它有三種取值:

          1. 設置為 0 的時候,表示每次事務提交時都只是把 redo log 留在 redo log buffer 中 ;
          2. 設置為 1 的時候,表示每次事務提交時都將 redo log 直接持久化到磁盤;
          3. 設置為 2 的時候,表示每次事務提交時都只是把 redo log 寫到 page cache。

          如果不是對性能要求高的,一般把該參數(shù)設置為 1

          redo log的擦除

          通過上邊的設計,速度可靠性的問題都解決了,但是我們仔細想想,還會有什么問題?

          隨著文件的增加,落盤的速度會越來越慢,直到有一天 ...

          聰明的設計者這樣子想著,如果我一直處理小文件,最大不能超過某個大小,不就行了?

          也確實是這樣子處理的,但是這里就涉及到一個刪除日志文件的算法,即我們的redo log擦除。

          redo log 的大小是固定的,比如可以配置一組4個文件,每個文件大小是8M,那么這個redo log總共就可以記錄32M的操作,這個參數(shù)可以通過innodb_log_file_size設置。

          下圖是具體的擦除算法,ib_logfile 從頭開始寫,寫到末尾就又回到開頭循環(huán)寫。

          擦除示意圖 - 來自丁奇MySQL連邊編輯

          write pos是當前記錄的位置,一邊寫一邊后移,寫到第3號文件末尾后就回到0號文件開頭。checkpoint是當前要擦除的位置,也是往后移動并且循環(huán)的,擦除記錄前要把記錄更新到數(shù)據(jù)文件,write pos與check point之間為剩余可用寫入的空間。

          何時會擦除redo log并更新到數(shù)據(jù)文件中

          1. 系統(tǒng)空閑時
          2. Redo log文件沒有空閑空間時,即write pos追上check point的時候;
          3. MySQL Server正常關閉時

          crash-safe

          有了以上這一些機制保障,我們可以相信redo log是可靠的,只要持久化到redo log文件中了,InnoDB 就可以保證即使數(shù)據(jù)庫發(fā)生異常重啟,之前提交的記錄都不會丟失,而我們把這個能力稱為 crash-safe。

          歸檔日志(binlog)

          在寫這篇文章的時候,糾結到底先寫redo log還是binlog,最后還是秉承先苦后甜的原則,把redo log寫在前面了。如果redo log的部分看懂了,binlog掌握是輕松的,跟著我的思路,我們繼續(xù)binlog~

          前邊講過,redo log是InnoDB引擎特有的日志,是引擎層面的日志,而在我們的數(shù)據(jù)庫的Server層面,也有自己的日志,稱為binlog(歸檔日志)。

          binlog是邏輯日志,怎么樣來理解這個邏輯日志呢?

          我們通過查看一段binlog來理解。

          理解邏輯日志

          這里一大段的操作,都是為了查看binlog文件里邊存儲的是什么內(nèi)容,熟悉的讀者可以直接略過。

          執(zhí)行命令,寫入新binlog文件,不讓之前的邏輯影響。

          執(zhí)行一次flush logs命令行,就會在data目錄下新增一個mysql-bin.00000x文件

          ## 登陸MySQL命令行
          mysql -uroot -p
          ## 刷新binlog
          flush logs;
          ## 確認刷新binlog成功
          show master status;
          ## 查詢binlog日志位置
           show variables like'log_bin%';
          data目錄下的mysql-bin文件

          測試數(shù)據(jù)

          ## 創(chuàng)建表
          CREATE TABLE `User`  (
            `id` int(11NOT NULL AUTO_INCREMENT,
            `name` varchar(10CHARACTER SET gb2312 COLLATE gb2312_chinese_ci NOT NULL,
            `age` int(11UNSIGNED NOT NULL,
            PRIMARY KEY (`id`USING BTREE
          ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Compact;

          ## 新增
          INSERT `User` VALUES("1""張三"18);
          INSERT `User` VALUES("2""李四"20);
          ## 修改
          DELETE FROM `User` WHERE id = 1;
          執(zhí)行語句截圖

          翻譯binlog二進制文件

          sudo /usr/local/mysql/bin/mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.000006 > mysqlbin.sql
          binlog翻譯

          這是翻譯出來的sql文件,是因為我在mysqlbinlog -v參數(shù)加工而成的。

          由此可知,邏輯日志里邊就是記錄著sql語句,通過sql語句記錄著邏輯的變化,比如insert, update等動作,但不是記錄具體數(shù)據(jù),那個由物理日志完成。

          與redo log的區(qū)別

          1. redo log是innoDB引擎特有的;binlog是MySQL的Server層實現(xiàn)的,所有引擎都能使用;
          2. redo log是循環(huán)寫的,空間固定會用完;binlog是追加寫入的?!白芳訉憽笔侵竍inlog文件寫到一定大小后會切換到下一個,并不會覆蓋以前的日志。

          binlog寫入策略

          通過與redo log的區(qū)別,我們知道,binlog是追加寫入的,所以與redo log寫入相比,沒有擦除的概念。那么,還有一些什么樣的其他的區(qū)別呢?

          binlog的寫入邏輯比較簡單:事務執(zhí)行過程中,先把日志寫到binlog cahce,事務提交的時候,再把binlog cache寫到binlog文件中(落盤)。

          binlog寫入

          從上圖可以看到,每個線程都有自己的binlog cache,但是共用同一份binlog文件。

          圖中的write,指的就是把日志寫入到數(shù)據(jù)庫系統(tǒng)的page cache,并沒有把數(shù)據(jù)持久化到磁盤,所有速度很快;

          圖中的fsync,才是將數(shù)據(jù)持久化到磁盤的操作。

          write 和 fsync 的時機,是由參數(shù) sync_binlog 控制的:

          sync_binlog=0 的時候,表示每次提交事務都只 write,不 fsync;

          sync_binlog=1 的時候,表示每次提交事務都會執(zhí)行 fsync;

          sync_binlog=N(N>1) 的時候,表示每次提交事務都 write,但累積 N 個事務后才 fsync。

          因此,在出現(xiàn) IO 瓶頸的場景里,將 sync_binlog 設置成一個比較大的值,可以提升性能。在實際的業(yè)務場景中,考慮到丟失日志量的可控性,一般不建議將這個參數(shù)設成 0,比較常見的是將其設置為 100~1000 中的某個數(shù)值。

          但是,將 sync_binlog 設置為 N,對應的風險是:如果主機發(fā)生異常重啟,會丟失最近 N 個事務的 binlog 日志。

          引用《極客時間MySQL45講》

          淺談兩階段提交

          這里講的兩階段提交,就是純粹的指redo log和binlog日志的兩階段提交。

          而兩階段提交的目的就是讓redo log和binlog兩個日志邏輯上一致。

          如果redo log持久化并進行了提交,而binlog未持久化數(shù)據(jù)庫就crash了,則從庫從binlog拉取數(shù)據(jù)會少于主庫,造成不一致。因此需要內(nèi)部事務來保證兩種日志的一致性。

          兩階段提交步驟

          兩階段提交
          1. 將語句執(zhí)行
          2. 記錄redo log,并將記錄狀態(tài)設置為prepare
          3. 通知Server,已經(jīng)修改好了,可以提交事務了
          4. 將更新的內(nèi)容寫入binlog
          5. commit,提交事務
          6. 將redo log里這個事務相關的記錄狀態(tài)設置為commited

          prepare: redolog寫入log buffer,并fsync持久化到磁盤,在redolog事務中記錄2PC的XID,在redolog事務打上prepare標識

          commit: binlog寫入log buffer,并fsync持久化到磁盤,在binlog事務中記錄2PC的XID,同時在redolog事務打上commit標識 其中,prepare和commit階段所提到的“事務”,都是指內(nèi)部XA事務,即2PC

          恢復步驟

          redolog中的事務如果經(jīng)歷了二階段提交中的prepare階段,則會打上prepare標識,如果經(jīng)歷commit階段,則會打上commit標識(此時redolog和binlog均已落盤)。

          1. 按順序掃描redolog,如果redolog中的事務既有prepare標識,又有commit標識,就直接提交(復制redolog disk中的數(shù)據(jù)頁到磁盤數(shù)據(jù)頁)
          2. 如果redolog事務只有prepare標識,沒有commit標識,則說明當前事務在commit階段crash了,binlog中當前事務是否完整未可知,此時拿著redolog中當前事務的XID(redolog和binlog中事務落盤的標識),去查看binlog中是否存在此XID
            1. 如果binlog中有當前事務的XID,則提交事務(復制redolog disk中的數(shù)據(jù)頁到磁盤數(shù)據(jù)頁)
            2. 如果binlog中沒有當前事務的XID,則回滾事務(使用undolog來刪除redolog中的對應事務)

          可以將mysql redolog和binlog二階段提交和廣義上的二階段提交進行對比,廣義上的二階段提交,若某個參與者超時未收到協(xié)調者的ack通知,則會進行回滾,回滾邏輯需要開發(fā)者在各個參與者中進行記錄。mysql二階段提交是通過xid進行恢復。

          總結

          這篇文章到這里就寫完了,本來這篇文章準備寫很長的,后來發(fā)現(xiàn)還是閱讀體驗更重要,文章的連貫性更重要,一篇文章不在乎能寫多少,而是讀者能吸收多少。
          衷心感謝每一位認真讀文章的人,我是連邊,專注于Java和架構領域,堅持撰寫有原理,有實戰(zhàn),有體系的技術文章。
          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  久久国产精品视频 | 福利操逼| 黄色激情视频网站 | 日韩黄色视屏 | 小美女逃课去操比 |