初探 InnoDB 存儲引擎的架構設計

前言

InnoDB 組件結構:

  1. buffer pool : 緩衝池,緩存磁盤的數據

  2. redo log buffer :記錄對緩衝池的操作,根據策略寫入磁盤防止宕機但事務已經提交而丟失數據

  3. undo log :當對緩衝池的數據進行修改時,在事務未提交的時候都可以進行回滾,將舊值寫入 undo 日誌文件便於回滾,此時緩衝池的數據與磁盤中的不一致,是髒數據

1. Buffer Pool

假設現在有一條更新語句:

update users set name = 'lisi' where id = 1

需要更新到數據庫,InnoDB 會執行哪些操作呢?

首先,InnoDB 會判讀緩衝池裏是否存在 id = 1 這條數據,如果不存在則從磁盤中加載到緩衝池中,而且還會對這行數據加獨佔鎖,防止多個 sql 同時修改這行數據。

2. undo 日誌文件

假設 id = 1 這條數據 name 原來的值 name = 'zhangsan',現在我們要更新爲 name = 'lisi' , 那麼我們就需要把舊值 name='zhangsan'和 id=1 這些信息寫入到 undo 日誌文件中。

對於熟悉數據庫的同學來說都瞭解事務的概念,在事務未提交之前,所有操作都有可能進行回滾,即可以把 name = 'lisi' 回滾到 name = 'zhangsan',所以將更新前的值寫到 undo 日誌文件。

3. 更新 buffer pool 數據

在 undo 日誌文件寫入完畢之後,便開始更新內存中的這條數據。把 id = 1 的 name = 'zhangsan' 更新爲 name = 'lisi'。這時內存中的數據已經更新完畢,但磁盤上的還沒有變化,此時出現了不一致的髒數據。

這時可能有一個疑問,萬一事務提交完成,但 MySQL 服務宕機了,而內存中的數據還沒寫入到磁盤,是不是會造成數據丟失而造成 sql 執行數據前後不一致?

4. redo log buffer

在 InnoDB 結構中,有一個 redo log buffer 緩衝區存放 redo 日誌,所謂 redo 日誌,例如 把 id=1,name='zhangsan'修改爲 name='lisi' 便是一條日誌。

但這時 redo log buffer 還僅僅存在內存中,沒能實現 MySQL 宕機後的數據恢復。

5. 事務沒提交,數據庫宕機後有影響嗎?

其實並沒有影響,事務沒有提交,意味着執行沒有成功,就算 MySQL 崩潰或者宕機後,內存中的 buffer pool 和 redo log buffer 修改過的數據都會丟失,也並不影響數據前後的一致性。如果事務提交失敗,那數據庫的數據更加不會改變。

6. 提交事務,redo 日誌的配置策略

在提交事務時,redo 日記會根據策略實現把 redo 日誌從 redo log buffer 裏寫入磁盤。策略通過 innoDB_flush_log_at_trx_commit 來配置。

  1. innoDB_flush_log_at_trx_commit 的參數爲 0,就算事務提交後,也不會把 redo 日誌寫入磁盤。MySQL 宕機後會內存中的數據會丟失。

  1. innoDB_flush_log_at_trx_commit 的參數爲 1,事務提交後,redo 日誌會從內存刷入磁盤,只要事務提交成功,redo log 就必然存在磁盤裏。

此時就算 buffer pool 的數據沒有刷進磁盤,也可以從 redo log 中得知修改過哪些數據,MySQL 宕機重啓後,可以從 redo 日誌中恢復修改的數據。

  1. innoDB_flush_log_at_trx_commit 的參數爲 2,事務提交後,redo log 僅僅停留在 os cache 中,還沒刷進磁盤,萬一此時服務宕機了。那麼 os cache 中的數據也會丟失,即使事務提交成功,也會造成數據丟失。

看完這幾種相信爲了保證數據安全,參數爲 1 是最佳策略。

7. 事務的最終提交,binlog

binlog 其實是屬於 MySQL Server 的日誌文件,而在這出提出是因爲與 redo log 有着很大的關聯。

1) biglog 與 redo log 的區別

2) 提交事務的時候同時寫入 binlog

在執行更新的同時,innoDB 與執行器一直在交互,包括加載數據到緩衝池,寫入 undo 日誌文件,更新內存數據,寫 redo 日誌和刷入磁盤等。而對 binlog 的寫入也是由執行器執行。

其中 1、2、3、4 步驟爲執行更新語句做的事,而 5、6 是提交事務開始做的事。

3) binlog 日誌刷盤策略分析

sync_binlog 參數控制 binlog 的刷盤策略

  1. sync_ binlog 默認值是 0,提交事務後,會把 binlog 日誌存在 os cache 中,MySQL 宕機後會造成 os cache 中數據的丟失

  2. sync_binlog 值爲 1,提交事務後,把 binlog 日誌直接刷入磁盤中。

4) 基於 binlog 和 redo log 完成事務的提交

binlog 寫入磁盤後,會把 binlog 日誌文件所在的位置和文件名稱都寫入 redo log 日誌文件中,同時在 redo log 日誌文件裏寫入一個 commit 標記。

5) commit 標記有什麼意義?

commit 標記意義着保持 redo log 和 binlog 日誌一致。如果在步驟 5 或者步驟 6,事務提交開始,MySQL 宕機了,redo log 中並沒有 commit 標記,都算事務提交失敗。

意味着 commint 標記是事務最終提交成功。

8. buffer pool 髒數據刷入磁盤

髒數據刷入磁盤是由後臺 IO 線程隨機刷入磁盤的。

這時候考慮到,在刷入磁盤之前,MySQL 宕機怎麼辦?這時候,事務已經提交成功,redo log 中也有 commit 標記,就算宕機了,重啓後,也會根據 redo 日誌文件把數據更新到內存中,等待 IO 線程的刷盤。

9. 總結

通過更新語句執行分析之後,瞭解到 InnoDB 存儲引擎中包含了 buffer pool 緩衝池、redo log buffer 緩衝區等緩存數據,undo、reod log 等日誌文件,同時也有 MySQL Server 的日誌文件。

在執行更新語句的時候,會修改 buffer pool、寫 undo 日誌文件、 寫 redo log buffer 等操作;提交事務時,會將 redo log 刷盤,binlog 刷盤,寫入 binlog 文件名稱和位置,寫入 commit 標記, 最後等待 IO 線程將 buffer pool 的髒數據隨機刷盤。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/u4PkGdEFFAui-f-s4DDfTw