十、MySQL redo log 和 bin log 概述

UPDATE test SET name = 'abc' WHERE id = 1;

更新語句的執行流程與查詢語句類似,同樣是需要經過下圖的流程。首先清空對應表的緩存,經過分析器,優化器,執行器最終操作存儲引擎修改表中數據。

在這裏插入圖片描述

除此之外,MySQL爲了保證在高併發更新數據時的效率以及數據的恢復能力,引入了兩個日誌模塊,redo logbin log

redo log

redo log實際上是用於提高更新效率的,尤其是短時間內有大量更新操作的時候,效果更加明顯。

考慮沒有redo log的情況下更新操作的流程效率。到達執行器階段,馬上查找到對應數據,然後進行一次IO操作寫入磁盤,這裏要注意的是一次磁盤IO操作是成本很高的。更新操作不頻繁的情況下也許勉強能應付,一旦操作非常頻繁就會導致大量的磁盤IO,效率也就很低了。

redo log正是爲了解決這個問題而引入的,首先,redo logInnoDB引擎內的。每一次的更新操作,都會記錄在redo log中,等數據庫空閒的時候,再將這些操作通過一次IO操作寫入表中。

這就像上課的時候可以將筆記臨時記錄在小本本上,回頭再整理到筆記本。記賬的時候也一樣,redo log就像這裏的小本本。

實際執行更新語句時,首先會記錄到buffer pool中,同時記錄到redo log buffer,之後再將數據刷入redo log(硬盤文件)中。

redo log寫入磁盤的情況有兩種,一種是數據庫空閒的時候,一種是redo log寫滿的時候,寫滿的時候會將一部分記錄寫入磁盤。

在這裏插入圖片描述

如圖,redo log是可以循環寫的,也就是寫完最後一個文件時會重新從第一個文件開始寫,因此這裏畫成了一個圈代表redo log,其中write pos指針表示當前寫的位置,check pos的位置表示當前寫入磁盤的開始位置,即這兩個指針中間空的部分是redo log中空閒的部分,若write pos追上check pos時,那麼就需要先將部分記錄寫入磁盤,check pos後移即可。

redo log相關參數中,innodb_log_file_sizeinnodb_log_files_in_group的乘積表示 redo log 的大小。

innodb_flush_log_at_trx_commit
0:表示每次事務提交時都只是把 redo log 留在 redo log buffer 中 ;
1:表示每次事務提交時都將 redo log 直接持久化到磁盤;
2:表示每次事務提交時都只是把 redo log 寫到 page cache。

mysql> show variables like '%innodb_log_file%';
+---------------------------+----------+
| Variable_name             | Value    |
+---------------------------+----------+
| innodb_log_file_size      | 50331648 |
| innodb_log_files_in_group | 2        |
+---------------------------+----------+
2 rows in set

mysql> show variables like 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1     |
+--------------------------------+-------+
1 row in set

bin log

MySQL想要具備恢復到過去某一時刻的能力,就需要有每次數據變動的歸檔記錄,bin log就是用於解決這個問題的,例如,數據庫每週進行一次整庫備份,假如前天剛做完整庫備份,要恢復到昨天某一時刻,只需要利用最近那次整庫備份和bin log即可恢復。

bin log 和 redo log 的區別:

  1. bin log是屬於Server層的,而redo log是屬於InnoDB特有的
  2. bin log記錄的是操作邏輯,即某時刻對錶xxx中ID=2這一行字段c加1。
  3. redo log 是循環寫的,而 bin log 是追加寫的,也就是不會覆蓋以前的數據

結合 bin log 的定位很容易就能理解上述的區別,bin log 是歸檔日誌,用於數據恢復的,恢復的時候需要按時間先後順序恢復(相當於記錄在 bin log 中的操作邏輯以整庫備份的基點開始重新執行一遍)。

而 redo log 是臨時性的,自然不需要像 bin log 那樣記錄完備。記錄的內容也比 bin log 簡單,因爲只要 redo log 中的髒數據落盤,就不在需要了。

兩階段提交

在瞭解了兩個日誌模塊後,再來看看一條更新語句會怎麼執行:

  1. 執行器操作引擎找到相關數據
  2. 執行器拿到引擎給的行數據,執行UPDATE操作,得到新的一行數據,再調用引擎接口寫入這行新數據。
  3. 引擎將這行新數據更新到內存中(buffer pool),同時將這個更新操作記錄到 redo log 裏面,此時 redo log 處於prepare 狀態。然後告知執行器執行完成了,隨時可以提交事務。
  4. 執行器生成這個操作的 binlog,並把 binlog 寫入磁盤。
  5. 執行器調用引擎的提交事務接口,引擎把剛剛寫入的 redo log 改成提交(commit)狀態,更新完成。

上述流程中,最關鍵的就是先寫 redo log 並置於準備狀態,再寫 bin log,提交事務並將 redo log 置於 commit 狀態。這就是兩階段提交。

爲什麼要這樣操作呢?

兩階段提交是爲了保證數據恢復時數據的準確性。

假設現在我們不使用兩階段提交,有兩種情況,先寫redo log,再寫bin log,或者反之。

先寫redo log,再寫bin log

當寫完 redo log時,數據庫異常重啓,這時候,表中數據可以通過 redo log 恢復,但是 bin log 沒有記錄歸檔,因此在數據恢復時,這一塊數據會不準確

先寫bin log,再寫redo log

同樣的,當寫完 bin log 之後異常重啓,由於沒有寫 redo log,因此表中數據還是原來的數據,沒能恢復,但是歸檔日誌中卻已經記錄了這個操作了。

因此兩階段提交實際上是保證了兩個日誌模塊邏輯上的一致性。

至於爲什麼會有兩個日誌,是因爲MySQL最開始默認的存儲引擎是MyISAM,而InnoDB是後來增加的,也就是說 bin log 比 redo log 出現的更早,由於 MyISAM 引擎沒有事務,而InnoDB是有事務的,因此需要有crash-safe的能力,所以就引入了redo log,爲了保證這兩個日誌的邏輯一致,就需要兩階段提交。

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