MySQL 日誌系統之 redo log 和 binlog

来源:https://www.cnblogs.com/wupeixuan/archive/2019/10/24/11734501.html
-Advertisement-
Play Games

之前我們瞭解了一條查詢語句的執行流程,並介紹了執行過程中涉及的處理模塊。一條查詢語句的執行過程一般是經過連接器、分析器、優化器、執行器等功能模塊,最後到達存儲引擎。 那麼,一條 SQL 更新語句的執行流程又是怎樣的呢? 首先我們創建一個表 user_info,主鍵為 id,創建語句如下: CREAT ...


之前我們瞭解了一條查詢語句的執行流程,並介紹了執行過程中涉及的處理模塊。一條查詢語句的執行過程一般是經過連接器、分析器、優化器、執行器等功能模塊,最後到達存儲引擎。

那麼,一條 SQL 更新語句的執行流程又是怎樣的呢?

首先我們創建一個表 user_info,主鍵為 id,創建語句如下:

CREATE TABLE `T` (
  `ID` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

插入一條數據:

INSERT INTO T VALUES ('2', '1');

如果要將 ID=2 這一行的 c 的值加 1,SQL 語句為:

UPDATE T SET c = c + 1 WHERE ID = 2;

前面介紹過 SQL 語句基本的執行鏈路,這裡把那張圖拿過來。因為,更新語句同樣會走一遍查詢語句走的流程。

MySQL基本架構示意圖

  1. 通過連接器,客戶端與 MySQL 建立連接
  2. update 語句會把 T 表上的所有查詢緩存結果清空
  3. 分析器會通過詞法分析和語法分析識別這是一條更新語句
  4. 優化器會決定使用 ID 這個索引(聚簇索引)
  5. 執行器負責具體執行,找到匹配的一行,然後更新
  6. 更新過程中還會涉及 redo log(重做日誌)和 binlog(歸檔日誌)的操作

其中,這兩種日誌預設在資料庫的 data 目錄下,redo log 是 ib_logfile0 格式的,binlog 是 xxx-bin.000001 格式的。

接下來讓我們分別去研究下日誌模塊中的 redo log 和 binlog。

日誌模塊:redo log

在 MySQL 中,如果每一次的更新操作都需要寫進磁碟,然後磁碟也要找到對應的那條記錄,然後再更新,整個過程 IO 成本、查找成本都很高。為瞭解決這個問題,MySQL 的設計者就採用了日誌(redo log)來提升更新效率。

而日誌和磁碟配合的整個過程,其實就是 MySQL 里的 WAL 技術,WAL 的全稱是 Write-Ahead Logging,它的關鍵點就是先寫日誌,再寫磁碟。

具體來說,當有一條記錄需要更新的時候,InnoDB 引擎就會先把記錄寫到 redo log(redolog buffer)裡面,並更新記憶體(buffer pool),這個時候更新就算完成了。同時,InnoDB 引擎會在適當的時候(如系統空閑時),將這個操作記錄更新到磁碟裡面(刷臟頁)。

redo log 是 InnoDB 存儲引擎層的日誌,又稱重做日誌文件,redo log 是迴圈寫的,redo log 不是記錄數據頁更新之後的狀態,而是記錄這個頁做了什麼改動。

redo log 是固定大小的,比如可以配置為一組 4 個文件,每個文件的大小是 1GB,那麼日誌總共就可以記錄 4GB 的操作。從頭開始寫,寫到末尾就又回到開頭迴圈寫,如下圖所示。

redolog迴圈寫

圖中展示了一組 4 個文件的 redo log 日誌,checkpoint 是當前要擦除的位置,擦除記錄前需要先把對應的數據落盤(更新記憶體頁,等待刷臟頁)。write pos 到 checkpoint 之間的部分可以用來記錄新的操作,如果 write pos 和 checkpoint 相遇,說明 redolog 已滿,這個時候資料庫停止進行資料庫更新語句的執行,轉而進行 redo log 日誌同步到磁碟中。checkpoint 到 write pos 之間的部分等待落盤(先更新記憶體頁,然後等待刷臟頁)。

有了 redo log 日誌,那麼在資料庫進行異常重啟的時候,可以根據 redo log 日誌進行恢復,也就達到了 crash-safe。

redo log 用於保證 crash-safe 能力。innodb_flush_log_at_trx_commit 這個參數設置成 1 的時候,表示每次事務的 redo log 都直接持久化到磁碟。這個參數建議設置成 1,這樣可以保證 MySQL 異常重啟之後數據不丟失。

日誌模塊:binlog

MySQL 整體來看,其實就有兩塊:一塊是 Server 層,它主要做的是 MySQL 功能層面的事情;還有一塊是引擎層,負責存儲相關的具體事宜。redo log 是 InnoDB 引擎特有的日誌,而 Server 層也有自己的日誌,稱為 binlog(歸檔日誌)。

binlog 屬於邏輯日誌,是以二進位的形式記錄的是這個語句的原始邏輯,依靠 binlog 是沒有 crash-safe 能力的。

binlog 有兩種模式,statement 格式的話是記 sql 語句,row 格式會記錄行的內容,記兩條,更新前和更新後都有。

sync_binlog 這個參數設置成 1 的時候,表示每次事務的 binlog 都持久化到磁碟。這個參數也建議設置成 1,這樣可以保證 MySQL 異常重啟之後 binlog 不丟失。

為什麼會有兩份日誌呢?

因為最開始 MySQL 里並沒有 InnoDB 引擎。MySQL 自帶的引擎是 MyISAM,但是 MyISAM 沒有 crash-safe 的能力,binlog 日誌只能用於歸檔。而 InnoDB 是另一個公司以插件形式引入 MySQL 的,既然只依靠 binlog 是沒有 crash-safe 能力的,所以 InnoDB 使用另外一套日誌系統——也就是 redo log 來實現 crash-safe 能力。

redo log 和 binlog 區別:

  1. redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實現的,所有引擎都可以使用。
  2. redo log 是物理日誌,記錄的是在某個數據頁上做了什麼修改;binlog 是邏輯日誌,記錄的是這個語句的原始邏輯。
  3. redo log 是迴圈寫的,空間固定會用完;binlog 是可以追加寫入的。追加寫是指 binlog 文件寫到一定大小後會切換到下一個,並不會覆蓋以前的日誌。

有了對這兩個日誌的概念性理解後,再來看執行器和 InnoDB 引擎在執行這個 update 語句時的內部流程。

  1. 執行器先找引擎取 ID=2 這一行。ID 是主鍵,引擎直接用樹搜索找到這一行。如果 ID=2 這一行所在的數據頁本來就在記憶體中,就直接返回給執行器;否則,需要先從磁碟讀入記憶體,然後再返回。
  2. 執行器拿到引擎給的行數據,把這個值加上 1,比如原來是 N,現在就是 N+1,得到新的一行數據,再調用引擎介面寫入這行新數據。
  3. 引擎將這行新數據更新到記憶體(InnoDB Buffer Pool)中,同時將這個更新操作記錄到 redo log 裡面,此時 redo log 處於 prepare 狀態。然後告知執行器執行完成了,隨時可以提交事務。
  4. 執行器生成這個操作的 binlog,並把 binlog 寫入磁碟。
  5. 執行器調用引擎的提交事務介面,引擎把剛剛寫入的 redo log 改成提交(commit)狀態,更新完成。

下圖為 update 語句的執行流程圖,圖中灰色框表示是在 InnoDB 內部執行的,綠色框表示是在執行器中執行的。

update語句執行流程

其中將 redo log 的寫入拆成了兩個步驟:prepare 和 commit,這就是兩階段提交(2PC)。

兩階段提交(2PC)

MySQL 使用兩階段提交主要解決 binlog 和 redo log 的數據一致性的問題。

redo log 和 binlog 都可以用於表示事務的提交狀態,而兩階段提交就是讓這兩個狀態保持邏輯上的一致。下圖為 MySQL 二階段提交簡圖:

MySQL二階段提交圖

兩階段提交原理描述:

  1. InnoDB redo log 寫盤,InnoDB 事務進入 prepare 狀態。
  2. 如果前面 prepare 成功,binlog 寫盤,那麼再繼續將事務日誌持久化到 binlog,如果持久化成功,那麼 InnoDB 事務則進入 commit 狀態(在 redo log 裡面寫一個 commit 記錄)

備註: 每個事務 binlog 的末尾,會記錄一個 XID event,標志著事務是否提交成功,也就是說,recovery 過程中,binlog 最後一個 XID event 之後的內容都應該被 purge。

日誌相關問題

怎麼進行數據恢復?

binlog 會記錄所有的邏輯操作,並且是採用追加寫的形式。當需要恢復到指定的某一秒時,比如今天下午二點發現中午十二點有一次誤刪表,需要找回數據,那你可以這麼做:

  • 首先,找到最近的一次全量備份,從這個備份恢復到臨時庫
  • 然後,從備份的時間點開始,將備份的 binlog 依次取出來,重放到中午誤刪表之前的那個時刻。

這樣你的臨時庫就跟誤刪之前的線上庫一樣了,然後你可以把表數據從臨時庫取出來,按需要恢復到線上庫去。

redo log 和 binlog 是怎麼關聯起來的?

redo log 和 binlog 有一個共同的數據欄位,叫 XID。崩潰恢復的時候,會按順序掃描 redo log:

  • 如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
  • 如果碰到只有 parepare、而沒有 commit 的 redo log,就拿著 XID 去 binlog 找對應的事務。

MySQL 怎麼知道 binlog 是完整的?

一個事務的 binlog 是有完整格式的:

  • statement 格式的 binlog,最後會有 COMMIT
  • row 格式的 binlog,最後會有一個 XID event

在 MySQL 5.6.2 版本以後,還引入了 binlog-checksum 參數,用來驗證 binlog 內容的正確性。對於 binlog 日誌由於磁碟原因,可能會在日誌中間出錯的情況,MySQL 可以通過校驗 checksum 的結果來發現。所以,MySQL 是有辦法驗證事務 binlog 的完整性的。

redo log 一般設置多大?

redo log 太小的話,會導致很快就被寫滿,然後不得不強行刷 redo log,這樣 WAL 機制的能力就發揮不出來了。

如果是幾個 TB 的磁碟的話,直接將 redo log 設置為 4 個文件,每個文件 1GB。

數據寫入後的最終落盤,是從 redo log 更新過來的還是從 buffer pool 更新過來的呢?

實際上,redo log 並沒有記錄數據頁的完整數據,所以它並沒有能力自己去更新磁碟數據頁,也就不存在由 redo log 更新過去數據最終落盤的情況。

  1. 數據頁被修改以後,跟磁碟的數據頁不一致,稱為臟頁。最終數據落盤,就是把記憶體中的數據頁寫盤。這個過程與 redo log 毫無關係。
  2. 在崩潰恢復場景中,InnoDB 如果判斷到一個數據頁可能在崩潰恢復的時候丟失了更新,就會將它讀到記憶體,然後讓 redo log 更新記憶體內容。更新完成後,記憶體頁變成臟頁,就回到了第一種情況的狀態。

redo log buffer 是什麼?是先修改記憶體,還是先寫 redo log 文件?

在一個事務的更新過程中,日誌是要寫多次的。比如下麵這個事務:

begin;
INSERT INTO T1 VALUES ('1', '1');
INSERT INTO T2 VALUES ('1', '1');
commit;

這個事務要往兩個表中插入記錄,插入數據的過程中,生成的日誌都得先保存起來,但又不能在還沒 commit 的時候就直接寫到 redo log 文件里。

因此就需要 redo log buffer 出場了,它就是一塊記憶體,用來先存 redo 日誌的。也就是說,在執行第一個 insert 的時候,數據的記憶體被修改了,redo log buffer 也寫入了日誌。

但是,真正把日誌寫到 redo log 文件,是在執行 commit 語句的時候做的。

以下是我截取的部分 redo log buffer 的源代碼:

/** redo log buffer */
struct log_t{
    char        pad1[CACHE_LINE_SIZE];
    lsn_t       lsn;        
    ulint       buf_free;   // buffer 內剩餘空間的起始點的 offset
#ifndef UNIV_HOTBACKUP
    char        pad2[CACHE_LINE_SIZE];
    LogSysMutex mutex;      
    LogSysMutex write_mutex;    
    char        pad3[CACHE_LINE_SIZE];
    FlushOrderMutex log_flush_order_mutex;
#endif /* !UNIV_HOTBACKUP */
    byte*       buf_ptr;    // 隱性的 buffer
    byte*       buf;        // 真正操作的 buffer
    bool        first_in_use;   
    ulint       buf_size;   // buffer大小
    bool        check_flush_or_checkpoint;
    UT_LIST_BASE_NODE_T(log_group_t) log_groups;

#ifndef UNIV_HOTBACKUP
    /** The fields involved in the log buffer flush @{ */
    ulint       buf_next_to_write;
    volatile bool   is_extending;   
    lsn_t       write_lsn;  /*!< last written lsn */
    lsn_t       current_flush_lsn;
    lsn_t       flushed_to_disk_lsn;
    ulint       n_pending_flushes;
    os_event_t  flush_event;    
    ulint       n_log_ios;  
    ulint       n_log_ios_old;  
    time_t      last_printout_time;

    /** Fields involved in checkpoints @{ */
    lsn_t       log_group_capacity; 
    lsn_t       max_modified_age_async;
    lsn_t       max_modified_age_sync;
    lsn_t       max_checkpoint_age_async;
    lsn_t       max_checkpoint_age;
    ib_uint64_t next_checkpoint_no;
    lsn_t       last_checkpoint_lsn;
    lsn_t       next_checkpoint_lsn;
    mtr_buf_t*  append_on_checkpoint;
    ulint       n_pending_checkpoint_writes;
    rw_lock_t   checkpoint_lock;
#endif /* !UNIV_HOTBACKUP */
    byte*       checkpoint_buf_ptr;
    byte*       checkpoint_buf; 
    /* @} */
};

redo log buffer 本質上只是一個 byte 數組,但是為了維護這個 buffer 還需要設置很多其他的 meta data,這些 meta data 全部封裝在 log_t 結構體中。

總結

這篇文章主要介紹了 MySQL 裡面最重要的兩個日誌,即物理日誌 redo log(重做日誌)和邏輯日誌 binlog(歸檔日誌),還講解了有與日誌相關的一些問題。

另外還介紹了與 MySQL 日誌系統密切相關的兩階段提交(2PC),兩階段提交是解決分散式系統的一致性問題常用的一個方案,類似的還有 三階段提交(3PC) 和 PAXOS 演算法。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 彈性盒子(Flexible Box/filebox)是一種當頁面需要適應不同的屏幕大小以及設備類型時確保元素擁有恰當的行為的佈局方式。引入彈性盒佈局模型的目的是提供一種更加有效的方式來對一個容器中的子元素進行排列、對齊和分配空白空間。 彈性盒子由彈性容器(父元素)和彈性子元素(可以一個或者多個)組合 ...
  • <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv... ...
  • 1、變數聲明且沒有賦值; 2、獲取對象中不存在的屬性時; 3、函數需要實參,但是調用時沒有傳值,形參是undefined; 4、函數調用沒有返回值或者return後沒有數據,接收函數返回的變數是undefined。 ...
  • 1、indexOf( 數組 || 字元串 ); 作用:返回數組中某個指定元素的位置 (下標)。 ('item',['start']) 兩個參數: item: 必選、查找的元素。 start: 可選參數,規定在字元串中開始檢索的位置,它的合法取值是StringObject.legth-1如省略該參數, ...
  • appendChild(): 擴充:使用 insertBefore()方法在指定的現有子節點之前插入新的子節點。 ...
  • canvas :可以理解為一個div,作用是一塊畫板 如果想要使用canvas,需要創建一個“畫家”。生成畫家的方法: Transparency(透明度) globalAlpha = transparencyValue: 這個屬性影響到 canvas 里所有圖形的透明度,有效的值範圍是 0.0 (完 ...
  • 場景 SpringCloud-使用路由網關統一訪問介面(附代碼下載): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/102733039 在上面已經實現使用路由網關統一訪問介面後,下麵使用路由網關的服務過濾功能。 註: 博客: h ...
  • 場景 SpringCloud-使用熔斷器儀錶盤監控熔斷: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/102673599 SpringCloud -創建統一的依賴管理: https://blog.csdn.net/BADAO_L ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...