一樣的,更新語句的流程和查詢語句同樣
可是更新還涉及到日誌模塊:redo log(重作日誌)和 binlog(歸檔日誌)
sql
redo log
innodb_flush_log_at_trx_commit 這個參數設置成 1 的時候,表示每次事務的 redo log 都直接持久化到磁盤。
寫日誌時,用到了WAL 技術,WAL 的全稱是 Write-Ahead Logging,它的關鍵點就是先寫日誌,再寫磁盤。
具體來講,當有一條記錄須要更新時,InnoDB引擎就會先把記錄寫到 redo log,並更新內存,這時更新就算完成。同時,在適當的時候,InnoDB 引擎會將這個操做記錄更新到磁盤裏面,而這個更新每每是在系統比較空閒的時候。
InnoDB 的 redo log 是固定大小的,好比能夠配置爲一組 4 個文件,每一個文件的大小是 1GB,那麼這塊「粉板」總共就能夠記錄 4GB 的操做。從頭開始寫,寫到末尾就又回到開頭循環寫,以下面這個圖所示。
數據庫
write pos 是當前記錄的位置,一邊寫一邊後移,寫到第 3 號文件末尾後就回到 0 號文件開頭。checkpoint 是當前要擦除的位置,也是日後推移而且循環的,擦除記錄前要把記錄更新到數據文件。write pos 和 checkpoint 之間的是「粉板」上還空着的部分,能夠用來記錄新的操做。若是 write pos 追上 checkpoint,表示「粉板」滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把 checkpoint 推動一下。
有了 redo log,InnoDB 就能夠保證即便數據庫發生異常重啓,以前提交的記錄都不會丟失,這個能力稱爲 crash-safe。
spa
binlog
binlog(歸檔日誌)是Server 層本身的日誌
爲何有兩份日誌:MySQL 自帶的引擎是 MyISAM,可是 MyISAM 沒有 crash-safe 的能力,binlog 日誌只能用於歸檔。而 InnoDB 是另外一個公司以插件形式引入 MySQL 的,使用另一套日誌系統——也就是 redo log 來實現 crash-safe 能力。
兩種日誌的不點:
插件
- redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實現的,全部引擎均可以使用。
- redo log 是物理日誌,記錄的是「在某個數據頁上作了什麼修改」;binlog 是邏輯日誌,記錄的是這個語句的原始邏輯,好比「給 ID=2 這一行的 c 字段加 1 」。
- redo log 是循環寫的,空間固定會用完;binlog 是能夠追加寫入的。「追加寫」是指 binlog 文件寫到必定大小後會切換到下一個,並不會覆蓋之前的日誌。
執行器和 InnoDB 引擎執行update 語句時的內部流程。日誌
- 執行器先找引擎取 ID=2 這一行。ID 是主鍵,引擎直接用樹搜索找到這一行。若是 ID=2 這一行所在的數據頁原本就在內存中,就直接返回給執行器;不然,須要先從磁盤讀入內存,而後再返回。
- 執行器拿到引擎給的行數據,把這個值加上 1,好比原來是 N,如今就是 N+1,獲得新的一行數據,再調用引擎接口寫入這行新數據。
- 引擎將這行新數據更新到內存中,同時將這個更新操做記錄到 redo log 裏面,此時 redo log 處於 prepare 狀態。而後告知執行器執行完成了,隨時能夠提交事務。
- 執行器生成這個操做的 binlog,並把 binlog 寫入磁盤。
- 執行器調用引擎的提交事務接口,引擎把剛剛寫入的 redo log 改爲提交(commit)狀態,更新完成。
流程圖以下:
redo log 的寫入包括兩個步驟,prepare 和 commit,這就是「兩階段提交」
blog
兩階段提交
爲何必須有「兩階段提交」呢?這是爲了讓兩份日誌之間的邏輯一致。
若是不用兩階段提交,先寫完 redo log 再寫 binlog,或者反過來,會有如下問題發生:
扔用update來舉例,假設當前ID=2的行,字段c的值是0,再假設執行 update 語句過程當中在寫完第一個日誌後,第二個日誌尚未寫完期間發生了 crash,會出現的狀況:
接口
- 先寫 redo log 後寫 binlog。假設在 redo log 寫完,binlog 尚未寫完的時候,MySQL 進程異常重啓。因爲咱們前面說過的,redo log 寫完以後,系統即便崩潰,仍然可以把數據恢復回來,因此恢復後這一行 c 的值是 1。可是因爲 binlog 沒寫完就 crash 了,這時候 binlog 裏面就沒有記錄這個語句。所以,以後備份日誌的時候,存起來的 binlog 裏面就沒有這條語句。而後你會發現,若是須要用這個 binlog 來恢復臨時庫的話,因爲這個語句的 binlog 丟失,這個臨時庫就會少了這一次更新,恢復出來的這一行 c 的值就是 0,與原庫的值不一樣。
- 先寫 binlog 後寫 redo log。若是在 binlog 寫完以後 crash,因爲 redo log 還沒寫,崩潰恢復之後這個事務無效,因此這一行 c 的值是 0。可是 binlog 裏面已經記錄了「把 c 從 0 改爲 1」這個日誌。因此,在以後用 binlog 來恢復的時候就多了一個事務出來,恢復出來的這一行 c 的值就是 1,與原庫的值不一樣。
小結:
redo log 和 binlog 均可以用於表示事務的提交狀態,而兩階段提交就是讓這兩個狀態保持邏輯上的一致。
Redo log是記錄這個頁 「作了什麼改動」。
Binlog有兩種模式,statement 格式的話是記sql語句, row格式會記錄行的內容,記兩條,更新前和更新後都有。
進程