---
# System prepended metadata

title: 一句更新在MySQL底層是如何運作的？
tags: [MySQL]

---

---
title: 一句更新在MySQL底層是如何運作的？
date: 2020-01-05 18:23:15
tags:
  - MySQL
thumbnail: /uploads/mysql.jpg
---

繼上一章「[一句查詢在MySQL底層是如何運作的？](https://blakefunteis.github.io/2020/01/05/how-run-a-select-in-mysql)」我們了解了MySQL在一句SQL查詢語句後，如何分析優化和執行該語句。

    客戶端 → 連接器 → 分析器 → 優化器 → 執行器
                ↘     ↙
                 查詢緩存

其實更新語句，同樣也會走這個流程。不過他少了查詢緩存，確切來說他不是少了查詢緩存，而是清除緩存，就如上一章我們所講，如果表有變更，他就會清除該表內的查詢緩存，所以才不建議使用查詢緩存，同時他跟查詢還有幾點不同，這個我們稍後會講到。

過去的時候，不管是工作還是在學進修的機會，我們有非常低的機率會遇到資料庫整個資料異常，需要恢復到一個禮拜前的資料，但是那時候沒有執行資料備份出成為sql檔案，那麼我們要如何把資料庫還原到一個禮拜前的狀態？

這時候就要說更新語句跟查詢語句不一樣的地方了，同時也是今天這主題的兩個主角，就是「[Redo Log 重做日誌](https://en.wikipedia.org/wiki/Redo_log)」和「[Binlog 歸檔日誌](https://baike.baidu.com/item/Mysql%20Binlog#1)」

### Redo Log 重做日誌
![](https://i.imgur.com/caXu728.jpg)

我們平常在外面消費會有記帳的習慣，在還沒有記帳APP的時候，都是先記錄在紙上，但是因為每天都會消費，所以帳越來越多，我們一定還會有一本記帳小本本，來專門記錄所有的帳。如果今天要記帳，那麼我可以有兩種做法
1. 直接拿出記帳小本本，紀錄這筆帳
2. 先記錄在紙上，等回家之後再記錄到記帳小本本上。

在外面很忙或者不好拿出記帳本的時候，我們肯定都是選擇二者，先記錄在紙上，等回家之後再紀錄到記帳本上，因為前者我們必須拿著一本比較重的記帳本，然後還要翻到那一頁的日期再進行紀錄。速度和方便性自然都是第二者比較快比較方便。

同樣的在MySQL也是有相同的問題，如果每次資料都是寫入硬碟（記帳小本本），然後硬碟（記帳小本本）還要找到相應的資料，之後硬碟（記帳小本本）在進行更新，整個IO成本都太高，為了解決這個問題，MySQL的作者也採用了類似上面的方式來解決這問題。

記帳紙和記帳本之間的關係，其實就是MySQL中的 **Write-Ahead Logging** 先寫日誌再寫硬碟，也就是先寫記帳紙，等回家之後或者能抽空的時候在寫記帳本。

當有一條資料要更新的時候，**InnoDB** 會先把紀錄寫進Redo Log（記帳紙）上，並且更新記憶體，到這邊更新其實就算完成了。同時，InnoDB會在適當的時候將這筆更新寫進硬碟，但是這個通常都是資料庫比較空閑的時候才會做的，就像我們回家才會把記帳紙內的帳目紀錄到記帳本上。

Redo Log也是有固定的大小，就像我們的記帳紙一樣，都會有寫滿的一天，那麼當Redo Log被寫滿了，就必須先把部分寫進硬碟，才能為之後新的紀錄騰出空間。這樣即使資料庫發生異常重啟，之前提交的紀錄也不會消失，這個也稱之為「Crash-Safe」。

### Binlog 歸檔日誌
Redo Log 是InnoDB引擎的特有日誌，而Server本身當然也有自己的日誌就是Binlog。

為什麼會有兩份日誌呢？
因為一開始MySQL並沒有InnoDB的引擎，MySQL自帶的是MyISAM，但是MyISAM沒有Crash-Safe的能力，Binlog又只能用於歸檔。而InnoDB是另一個公司以插件的形式導入MySQL的。既然依靠Binlog無法實現Crash-Safe，那麼InnoDB只好使用另一套日誌Redo Log來實現Crash-Safe，這就是為什麼有兩套日誌的原因。

#### Redo Log和Binlog的差異
1. Redo log是InnoDB獨有，而Binlog是MySQL內建的。
2. Redo log紀錄的是“做了什麼修改”；Binlog則是“某ID=10的欄位A改成100”
3. Redo log是循環寫的，空間會用完，如果要再新增，那麼就要把之前的一些紀錄清除掉；Binlog寫到一定大小後會切換到下一個，並不會覆蓋之前所寫的

了解兩個日誌，我們來看看InnoDB在Update語句時候的流程是什麼
1. 執行器先找InnoDB取出例如ID等於10的，因ID是主鍵，所以會用樹搜索到這一筆資料，如果資料本來就在記憶體中就直接返回給執行器，如果沒有，則從硬碟中取出在寫進記憶體並且返回。
2. 執行器拿到資料後把資料變更為Update所要更改的資料成為新資料，之後直接調用InnoDB寫入新資料。
3. InnoDB將這筆資料寫入記憶體中，同時也會將這筆操作寫入Redo log中。
4. 執行器產生這筆操作的binlog，並且把binlog寫入硬碟。
5. 執行器調用InnoDB的提交接口，InnoDB把剛剛寫入的Redo log改成提交的狀態。

到此一句簡單的更新完成。