一:更新流程
- 對於更新來說,也同樣會根據 SQL 的執行流程進行。
-
- 連接器
- 連接數據庫,具體的不做贅述。
- 查詢緩存
- 在一個表上有更新的時候,跟這個表有關的查詢緩存會失效。
- 這也就是我們一般不建議使用查詢緩存的原因。
- 分析器
- 接下來,分析器會通過詞法和語法解析知道這是一條更新語句。
- 優化器
- 優化器決定要使用 ID 這個索引。
- 執行器
- 然后,執行器負責具體執行,找到這一行,然后更新。
- PS
- 與查詢流程不一樣的是,更新流程還涉及兩個重要的日志模塊,它們正是我們今天要討論的主角:
- redo log(重做日志)和 binlog(歸檔日志)。
二: redo log
- Mysql 更新存在的問題
- 在 MySQL 中,如果每一條更新記錄,都需要寫入磁盤。
- 然后磁盤也要找到對應的那條記錄,然后再更新,整個過程 IO 成本、查找成本都很高。
- 為了解決這個問題,MySQL 的設計者就用了 redo log 的概念來解決這個問題。
- redo log 概述
- redo log 是 InnoDB 獨有的,屬於 存儲引擎層的
- redo log 工作流程
- 具體來說,當有一條記錄更新的時候,InnoDB 引擎就會先把記錄寫到 redo log 里面,並更新內存,這個時候更新就算完成了。
- 同時,InnoDB 會在適當的時候,將這個操作記錄更新到磁盤里面.
- PS
- InnoDB 的 redo log 是固定大小的
- 比如可以配置為一組 4 個文件,每個文件的大小是 1GB,那么總共就可以記錄 4GB 的操作。
- 從頭開始寫,寫到末尾就又回到開頭循環寫
- 當 redo log 被寫滿時候,這時候不能在執行新的更新,必須停下來,擦除一些 redo log 的日志,寫入磁盤
- 有了 redo log,InnoDB 就可以保證即使數據庫發生異常重啟,之前提交的記錄都不會丟失,這個能力稱為 crash-safe
三 :binlog
- binlog 概述
- binlog(歸檔日志) 是 Server 層特有的
- 為什么會有兩份日志?
- 因為最開始 MySQL 里並沒有 InnoDB 引擎。
- MySQL 自帶的引擎是 MyISAM,但是 MyISAM 沒有 crash-safe 的能力,binlog 日志只能用於歸檔。
- 而 InnoDB 是另一個公司以插件形式引入 MySQL 的,既然只依靠 binlog 是沒有 crash-safe 能力的
- 所以 InnoDB 使用另外一套日志系統-- 就是 redo log 來實現 crash-safe 能力。
四 :binlog 和 redo log 的不同
- redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 層實現的,所有引擎都可以使用。
- redo log 是物理日志,記錄的是“在某個數據頁上做了什么修改”
- binlog 是邏輯日志,記錄的是這個語句的原始邏輯,比如“給 ID=2 這一行的 c 字段加 1 ”。
- redo log 是循環寫的,空間固定會用完;
- binlog 是可以追加寫入的。“追加寫”是指 binlog 文件寫到一定大小后會切換到下一個,並不會覆蓋以前的日志。
五:再看 Update 執行過程
- 例如
- mysql> update T set c=c+1 where ID=2;
- 流程
- 執行器先找引擎取 ID=2 這一行。
- ID 是主鍵,引擎直接用樹搜索找到這一行。
- 如果 ID=2 這一行所在的數據頁本來就在內存中,就直接返回給執行器
- 否則,需要先從磁盤讀入內存,然后再返回。
- 執行器拿到引擎給的行數據
- 把這個值加上 1,比如原來是 N,現在就是 N+1,得到新的一行數據,再調用引擎接口寫入這行新數據。
- 引擎將這行新數據更新到內存中
- 同時將這個更新操作記錄到 redo log 里面,此時 redo log 處於 prepare 狀態。
- 然后告知執行器執行完成了,隨時可以提交事務。
- 執行器生成這個操作的 binlog,並把 binlog 寫入磁盤。
- 執行器調用引擎的提交事務接口,引擎把剛剛寫入的 redo log 改成提交(commit)狀態,更新完成。
-
六:redo 的 “兩階段提交”
- 根據上面的流程圖,可以看到,redo log 的寫入拆成了兩個步驟:prepare 和 commit,這就是"兩階段提交"。
- 原因
- 由於 redo log 和 binlog 是兩個獨立的邏輯
- 如果不用兩階段提交,要么就是先寫完 redo log 再寫 binlog,或者采用反過來的順序,會導致數據的不一致出現。
- 簡單說,redo log 和 binlog 都可以用於表示事務的提交狀態,而兩階段提交就是讓這兩個狀態保持邏輯上的一致。
- 舉例一個完整的交易流程
- 賬本記上 賣一瓶可樂(redo log為 prepare狀態),然后收錢放入錢箱(bin log記錄)然后回過頭在賬本上打個勾(redo log置為commit)表示一筆交易結束。
- 如果收錢時交易被打斷,回過頭來整理此次交易,發現只有記賬沒有收錢,則交易失敗,刪掉賬本上的記錄(回滾)
- 如果收了錢后被終止,然后回過頭發現賬本有記錄(prepare)而且錢箱有本次收入(bin log),則繼續完善賬本(commit),本次交易有效。
七:小結
- redo log 用於保證 crash-safe 能力。
- innodb_flush_log_at_trx_commit 這個參數設置成 1 的時候,表示每次事務的 redo log 都直接持久化到磁盤。
- 這個參數建議設置成 1,這樣可以保證 MySQL 異常重啟之后數據不丟失。
- sync_binlog 這個參數設置成 1 的時候,表示每次事務的 binlog 都持久化到磁盤。
- 這個參數也建議設置成 1,這樣可以保證 MySQL 異常重啟之后 binlog 不丟失。
- 還介紹了與 MySQL 日志系統密切相關的“兩階段提交”。
- 兩階段提交是跨系統維持數據邏輯一致性時常用的一個方案,即使你不做數據庫內核開發,日常開發中也有可能會用到。