詳解MySQL更新語句的執(zhí)行流程
前言
在這篇文章中,小魚將介紹更新語句的執(zhí)行流程,從中我們又能學到什么呢?
SQL 的更新流程
我們先創(chuàng)建一張表作為演示表,作為演示表只需要一個主鍵、一個額外字段就可以了。下面是演示表的創(chuàng)建語句:
CREATE TABLE test(ID int primary key, age int);
如果我們需要將 ID=2 目標值自增 1,更新的 SQL 語句如下。
UPDATE test SET age=age+1 WHERE ID=2;
更新目標值時,得先查找的該行數(shù)據,所以也會執(zhí)行SQL查詢語句的流程。
- 在執(zhí)行語句前,連接上數(shù)據庫(連接器)。
- 因為是更新語句,涉及到更新目標表的查詢緩存會失效,所以該語句會把 test 表所有緩存結果都清空。
- 分析器通過詞法分析和語法分析知道該語句為更新語句。
- 優(yōu)化器決定要使用 ID 索引。
- 最后,執(zhí)行器負責更新的具體執(zhí)行,根據索引找到目標行,再執(zhí)行更新。
更新語句流程與查詢語句流程不一樣的地方在于日志模塊,更新語句涉及到兩個十分重要的日志模塊——redo log(重做日志)和 binlog(歸檔日志)。
Redo log 重做日志
??Redo Log??稱為重做日志,提供再寫入操作,恢復提交事務修改的頁操作,用來保證事務的持久性。
mysql 數(shù)據是被持久化寫進磁盤的,每次更新也需要找到目標數(shù)據,在進行修改,每次更新都執(zhí)行一遍該操作,這個過程的 IO 成本是比較高的。
為了解決這個問題,MySQL 采用了先寫日志,空余時間再寫磁盤的思路來提升更新效率。即是 WAL 技術(預寫式日志,WAL 的全稱是 Write-Ahead Logging)。
具體來說,當有更新語句執(zhí)行的時候,InnoDB 引擎會先把更新記錄寫到 redo log 日志里,并更新內存,這個時候已經完成更新(內存上),實際磁盤上的數(shù)據尚未更新。等適當?shù)臅r候(通常是系統(tǒng)空閑的時候),InnoDB 引擎會將這個操作記錄(redo log 中記錄的更新語句)更新到磁盤。
寫 redo log 的流程如下:
- 先將目標原始數(shù)據從磁盤中讀入內存中來,修改數(shù)據的內存拷貝。
- 生成一條重做日志并寫入
Redo Log Buffer,記錄的是數(shù)據被修改后的值。 - 當事務 commit 時,將
Redo Log Buffer中的內容采用追加寫的方式刷新到Redo Log File。 - 定期將內存中修改的數(shù)據刷新到數(shù)據文件(磁盤)中。
這樣做還有一個問題,InnoDB 的 redo log 日志的大小是固定的,它設計的是循環(huán)的,即日志文件寫滿后會覆蓋掉最先的記錄(從頭開始寫,寫到末尾就又回到開頭循環(huán)寫)。

write pos:當前記錄的位置,一邊寫一邊后移,當寫到第 3 號文件末(末尾)時會回到 0 號文件(開頭)開頭。checkpoint:當前要擦除的位置,同樣是往后推移并且循環(huán)的,擦除記錄前要把記錄更新到數(shù)據文件(更新到磁盤里)。write pos和checkpoint之間:redo log日志文件還空著的部分,可以用來記錄新的操作。- 如果
write pos追上checkpoint,表示redo log日志文件寫滿了,此時不能再執(zhí)行新的更新操作,會將記錄寫入數(shù)據文件,并執(zhí)行擦除記錄,推進checkpoint位置。
- 如果
試想:對于已經寫入 redo log 的記錄,在數(shù)據庫異常重啟后,能否恢復?
mysql 重啟后,已經寫入 redo log 的記錄不會丟失,這個能力也稱為 crash-safe。
crash-safe 還有個重要的日志——Binlog 日志。
Binlog 歸檔日志
MySQL 架構分為 Server 層和存儲引擎層,redo log 是存儲引擎層產生的日志,而 server 層也有日志——Binlog 歸檔日志。
兩者的區(qū)別在于以下幾點:
Redo log是 InnoDB 引擎特有的;binlog是 MySQL 的 Server 層產生的,任何引擎都存在該日志。Redo log是循環(huán)寫的,空間固定會用完,用完即從頭開始寫。binlog是追加寫,即binlog文件寫到一定大小后會新建日志文件,不會覆蓋掉以前的日志。redo log會不斷記錄,而binlog只有在事務提交的時候才記錄。Redo log是物理日志,詳細記錄了“在某個數(shù)據頁上做了什么修改”(包含事務的過程操作);binlog 是邏輯日志,記錄的是語句的原始邏輯(對數(shù)據最終的影響)。
譬如:一個事務對表做10萬行的記錄插入,在事務執(zhí)行過程中,會一直不斷的往 Redo Log 順序寫,而這個過程 Binlog 不會記錄,直至這個事務提交的時候,才會寫入到 Binlog 文件中。
這兩份日志存在的意義就是實現(xiàn) crash-safe 能力。
這兩個日志文件結合起來,才真正實現(xiàn)了 crash-safe 能力,讓 MySQL 既能保證事務的 ACID 屬性,又能支持高效的數(shù)據復制和恢復能力。
redo log 和 binlog 設置
查看 redo log 和 binlog 設置
show variables like 'innodb_flush_log_at_trx_commit'; show variables like 'sync_binlog'
innodb_flush_log_at_trx_commit:- 設置成 0,在提交事務時,InnoDB 不會立即觸發(fā)將緩存日志寫到磁盤文件的操作,而是每秒觸發(fā)一次緩存日志回寫磁盤操作,并調用操作系統(tǒng) fsync 刷新 IO 緩存。
- 設置成 1 ,每次事務的 redo log 都直接持久化到磁盤,如此可以保證 MySQL 在異常重啟后數(shù)據不會丟失。
- 設置成 2,在每個事務提交時,InnoDB 立即將緩存中的 redo 日志回寫到日志文件,但并不馬上調用 fsync 來刷新 IO 緩存,而是每秒只做一次磁盤 IO 緩存刷新操作。
sync_binlog這個參數(shù)設置成 1 的時候,表示每次事務的 binlog 都持久化到磁盤。對于需要高度數(shù)據持久性和不能承受數(shù)據丟失的系統(tǒng),建議將sync_binlog設置為1。
設置 redo log 和 binlog 配置
可以在 MySQL 配置文件(通常是 my. Cnf 或 my. Ini)中設置這個變量。設置好后需要重啟 mysl,使得配置生效。
[mysqld] sync_binlog=1 innodb_flush_log_at_trx_commit = 1
或者,也可以在 MySQL 運行時動態(tài)設置,但是這種變更只對新的會話有效,對于已經存在的會話,該設置直到會話結束才會生效。
SET GLOBAL sync_binlog=1; SET GLOBAL innodb_flush_log_at_trx_commit = 1;
以上就是詳解MySQL更新語句的執(zhí)行流程的詳細內容,更多關于MySQL更新語句的資料請關注腳本之家其它相關文章!

