mysql指引(十四):mysql和innodb的結合點:存儲引擎接口、binlog與redo

借用前文mysql指引(二):mysql邏輯結構和整體處理流程的圖,我們再來回顧下mysql的基本結構:

主要有兩塊構成:
  1. mysql server 層
  2. mysql 存儲引擎層

由於底層引擎層可以進行更換,故和軟件開發一樣,server層需要制定好接口定義,便於不同的接口實現(存儲引擎)接入。如下圖所示:

這樣就和我們平時開發軟件一樣,基於接口編程。對 server 層來說,屏蔽底層不同存儲引擎實現的細節。對於存儲引擎來說,根據接口定義,只要實現了接口,就意味着可以被融入到 mysql 的體系中。最終實現了插件式的存儲引擎。

來看下官方是怎麼說的:

The storage engines manage data storage and index management for MySQL. The MySQL server communicates with the storage engines through a defined API.

Each storage engine is a class with each instance of the class communicating with the MySQL server through a special handler interface.

這裏的API即接口定義,我們來看幾個。

查找類方法:

index_first:Retrieve first row in index and return.

index_next:Return next row in index.

index_read:Find a row based on a key and return.

records_in_range:For the given range how many records are estimated to be in this range.Used by optimizer to calculate cost of using a particular index.

增刪改方法:update_row、delete_row、write_row

其中,優化器就是調用 records_in_range 方法來計算掃描行數的。

知道了上述這些方法後,感興趣可以更進一步,來定製屬於自己的存儲引擎。


下面要說的是關於 binlogredo log 這兩種日誌。在 mysql server 層,引入了binlog來做歸檔日誌,主要可用於備份恢復,同時也被應用到主從同步等場景。在 mysql 引擎層,innodb引擎引入了 redo log,主要用來做崩潰恢復。

這塊內容關鍵要理解下面幾點:

  1. binlog的備份恢復是如何實現的
  2. redo log的崩潰恢復是如何實現的
  3. 爲什麼需要兩階段提交
  4. 刷盤

其他關於 binlog 的部分,會在分佈式 mysql 中繼續說明。


binlog日誌相關的網上有很多解釋,這裏只列舉幾個關鍵點。Mysql最初的引擎是myisam,Mysql 只在 server 層提供 binlog 記錄,存儲數據庫執行的操作日誌,起到歸檔作用。

binlog日誌有三種格式:

  1. statement格式:可理解爲直接記錄執行的sql語句
  2. row格式:可理解爲記錄對應的行數據
  3. Mix格式:就是前兩者的組合,會自動判斷採用哪種

當事務提交時,會將日誌內容按照一定格式追加寫入磁盤。不會覆蓋,所以可以作爲備份用。恢復時按照對應格式解析更新即可。


但是這裏面有一個問題,假如兩個事務A和B,期間都對記錄1執行了更新類的操作,假如A先更新記錄1,然後B再更新記錄1。A晚於B提交事務,那麼最終binlog中就會先記錄B的更新動作,然後纔是A的更新動作。

這樣,在從機上同步或者恢復時,就會出現問題。

但,真的會成這樣子嗎?實際上,前文學習了當前讀、鎖等概念後,你應該知道這裏的描述是錯誤的。事務B會在更新記錄1時被事務A阻塞,知道事務A提交爲止。


另外一個問題是,我們說的寫到磁盤,是真的落盤了嗎?實際上不一定。日誌寫到binlog中,無非是將日誌數據從Mysql的緩存(實際上爲 binlog cache)搬移到了操作系統的緩存中(實際上爲 page cache)。至於page cache中的數據何時落盤,就依賴操作系統的管理。

當然執行底層調用 fsync() 時就會將緩存數據寫入磁盤,纔會產生磁盤IO。

可以回顧下講解 innodb 基本結構的章節,裏面官方圖中,就有一個標註:O_DIRECT ,即直接寫入磁盤,不走操作系統緩存。


最後一個問題是,binlog的結構。binlog文件的組成單位是 binlog event ,每個binlog event 中包含了具體的日誌內容,這些內容格式就分爲了row、statement這種。實際上不需要了解 binlog event 的具體構成細節,類比數據頁結構即可。

來看幾個典型的binlog event:

  • query event:在statement格式下,裏面包含了對應的sql語句;在row格式下,對應的是begin語句;
  • gtid event:記錄GTID版本號(分佈式mysql會說到)
  • xid event:當事務提交時,會記錄該事件。

那麼,redo log的作用呢?這點前文已經講過了,用來做宕機恢復。


關於binlog 和 redo log這塊,總會出現兩階段提交的討論。指的是redo log 先寫成 prepare 狀態,然後binlog寫入,最後redo log 變成commit狀態。

重點是爲什麼一定需要兩階段提交?不執行兩階段提交的話,主要矛盾點在於:

  • redo log 做主機崩潰恢復
  • binlog 做從機同步

如果 redo log 和 binlog 不同步,那麼主機宕機恢復後,就和從機的同步數據不一致,這點是不能允許的。

redo log 和 binlog 都是記錄事務的,所以二者之間是根據事務ID關聯起來的。 binlog 中 類型爲 XID 的event 中就包含了事務ID,和redo log 中的事務ID一致。

所以,假如 redo log 最後是 prepare 狀態,那麼崩潰恢復時,會根據 redo 中某個事務的事務ID,去 binlog 中查找對應的事務,判斷該事務是否具有XID event,不具有則說明該事務未能正確提交,故無需恢復本事務。


最後,我們來簡單看下崩潰恢復的整體流程:

  1. 回滾未能 prepare 的事務
  2. 記錄已 prepare 但是未能 commit 的事務
  3. 前兩步就把 redo log 中的事務掃描並準備好了
  4. 因爲 binlog 是追加寫入,而崩潰是發生在一瞬間的,故mysql會讀取最後一個binglog文件,並判斷是否需要執行崩潰恢復流程
  5. 如果需要恢復,則會依次掃描 binlog event,記錄已經提交的事務ID(即有 XID event)。
  6. 掃描結束後對比 redo log中的記錄的已 prepare,未commit的事務,將匹配到的事務執行 commit 操作,將未能匹配的執行回滾操作。

本篇主要目的是將 mysql 和 innodb 結合起來,針對 binlog 和存儲引擎接口進行說明。

接下來,就要開啓全新的篇章,即分佈式 mysql。從我們已經熟悉的 mysql 上手,理解分佈式領域的一些關鍵概念。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章