MySQL更新過程

1. 前言

一條簡簡單單的更新操作涉及到太多的知識點了,首先我們要1.瞭解MySQL各個功能模塊,然後在MySQL更新時不僅僅是操作表數據還要操作2.日誌系統redo log、binlog和undo log,更新操作也不是實時更新到磁盤的而是通過3.Write-Ahead Logging機制先刷到內存再刷到磁盤,要刷到內存寫日誌的順序又涉及到4.二階段提交

2. 更新流程

首先是MySQL的各個功能模塊,在MySQL查詢過程會詳細介紹。

  1. 數據庫連接,然後通過分析器進行詞法分析和語法分析,再經過優化器選擇索引等。
  2. 執行器先找引擎取ID=2這一行。 ID是主鍵, 引擎直接用樹搜索找到這一行。 如果ID=2這一
    行所在的數據頁本來就在內存中, 就直接返回給執行器; 否則, 需要先從磁盤讀入內存, 然
    後再返回。
  3. 執行器拿到引擎給的行數據, 把這個值加上1, 比如原來是N, 現在就是N+1, 得到新的一行
    數據, 再調用引擎接口寫入這行新數據。
  4. 引擎將這行新數據更新到內存中, 同時將這個更新操作記錄到redo log裏面, 此時redo log處
    於prepare狀態。 然後告知執行器執行完成了, 隨時可以提交事務。
  5. 執行器生成這個操作的binlog, 並把binlog寫入磁盤。
  6. 執行器調用引擎的提交事務接口, 引擎把剛剛寫入的redo log改成提交(commit) 狀態, 更
    新完成。

3. MySQL日誌系統

在MySQL中,有三種日誌。分別是redo log、binlog和undo log。

3.1 redo log 和 binlog

1、存儲的內容

可以這樣理解,binlog記載的是update/delete/insert這樣的SQL語句,而redo log記載的是物理修改的內容(xxxx頁修改了xxx)。所以在搜索資料的時候也會有這樣的說法:binlog 記錄的是數據的邏輯變化redo log 記錄的是數據的物理變化

2、功能

redo log的作用是實現持久化。數據庫更新寫完內存,如果數據庫掛了,那我們可以通過redo log來恢復內存還沒來得及刷到磁盤的數據,將redo log加載到內存裏邊,那內存就能恢復到掛掉之前的數據了。

binlog的作用是進行複製和恢復

  • 主從服務器需要保持數據的一致性,通過binlog來同步數據。
  • 如果整個數據庫的數據都被刪除了,binlog存儲着所有的數據變更情況,那麼可以通過binlog來對數據進行恢復。

3、載體

redo log是InnoDB引擎特有的。binlog是MySQL的Server層實現的,所有引擎都可以使用。

4、記錄方式

redo log是循環寫的,空間固定會用完;binlog是追加寫入的,一個文件寫滿後會切換到下一個文件而不會去覆蓋以前的日誌。

3.2 undo log

undo log主要有兩個作用:回滾和多版本併發控制(MVCC)

在數據修改的時候,不僅記錄了redo log,還記錄undo log,如果因爲某些原因導致事務失敗或回滾了,可以用undo log進行回滾undo log主要存儲的也是邏輯日誌,比如我們要insert一條數據了,那undo log會記錄的一條對應的delete日誌。我們要update一條記錄時,它會記錄一條對應相反的update記錄。

回滾的實現就是找到undo log中對應的相反操作語句執行。
而多版本併發控制則是利用undo log做版本的回退(聊MVCCC時再具體講)

4. Write-Ahead Logging機制

  • 使用日誌,存儲引擎可以在內存中更新數據,然後將更新持久化到磁盤的日誌文件中,不需要每次都將更新後的數據刷新到磁盤(隨機IO)

  • 日誌採用的是追加方式,寫日誌的操作實在磁盤上一小塊區域的順序IO(比隨機IO快得多)

  • 日誌持久化到磁盤後,內存中被修改的數據(髒頁)在後臺可以慢慢刷新到磁盤

5. 二階段提交

上圖執行過程中,redo log分爲prepare階段和commit階段,在寫入binlog的前後執行,這就是二階段提交。

當commit 命令執行時,

  • 寫入redo log進入prepare階段:事務進入commit prepare 階段,事務中新生成的redo log 會被刷到磁盤
  • 寫入bin log:把binlog日誌刷到磁盤
  • redo log處於commit狀態更新完成:innodb釋放鎖,清除undo信息,設置redo log提交狀態。

5.1 爲什麼需要二階段提交

由於存在redo log 和 binlog ,而他們兩是相互獨立的。而事務提交必須確保兩者同時有效。不然會出現不一致的情形。我們對redo log和binlog不進行二階段提交的順序進行假設。

**先寫redo log再寫binlog:**redo log寫了,binlog還沒寫,數據庫崩了。通過redo log恢復數據庫能將這條事務執行,但是binlog沒有記錄,從數據庫就不能執行這條事務(或者在對數據庫回到某個點時會沒有這條事務),造成不一致的情況。

**先寫binlog再寫redo log:**binlog寫了,redo log還沒寫,數據庫崩了。通過redo log恢復數據庫沒有這條事務,但是binlog記錄了,從數據庫會執行這條事務(或者在對數據庫回到某個點時會有這條事務的執行),造成不一致的情況。

5.2 二階段提交怎麼解決問題

上圖的①時出現問題怎麼解決?

這個時候redo log已經到磁盤了。binlog沒有刷到磁盤所以會消失。服務器從故障中恢復時,讀取磁盤中的redo log ,但是由於對應的redo log項還是prepare狀態,就要判斷binlog 是否完整,如果binlog完整則提交事務,如果binlog不完整則回滾事務

上圖的②時出現問題怎麼解決?

這個時候redo log 和 binlog都已經存磁盤,服務器從redo log恢復就好了。

怎麼判斷binlog的完整性?

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

注:binlog有三種模式:statement、row和mixed(statement和row的混合)

找到redo log後怎麼找到binlog?

redo log和binlog有一個共同的數據字段,叫 XID。崩潰恢復的時候,會按順序掃描 redo log:如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;如果碰到只有 parepare、而沒有 commit 的 redo log,就拿着 XID 去 binlog 找對應的事務。

參考文章:

淺談mysql的兩階段提交協議
MySQL實戰45講——丁奇

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