日志系統 SQL更新語句的執行
更新語句執行的流程
更新語句執行的流程和查詢語句執行的流程一樣
注意:
在一個表上有更新的操作的時候,和這個表相關的查詢緩存就會被清空
在經歷分析器,優化器,和執行器存儲引擎的歷程中,還多了重要的日志模塊
Redo log 重做日志
Bin log 歸檔日志
Redolog 物理日志
是innodb 引擎獨有的日志模塊
它的關鍵點就是更新的時候先寫日志,再寫磁盤,在任務不忙的時候將大量的積累更新操作一塊兒進行IO寫入磁盤
InnoDB的redo log是固定大小的,比如可以配置為一組4個文件,每個文件的大小是1GB,那么這塊"粉板"總共就可以記錄4GB的操作。從頭開始寫,寫到末尾就又回到開頭循環寫,如下面這個圖所示
write pos是當前記錄的位置,一邊寫一邊后移,寫到第3號文件末尾后就回到0號文件開頭。checkpoint是當前要擦除的位置,也是往后推移並且循環的,擦除記錄前要把記錄更新到數據文件。
write pos和checkpoint之間的是"粉板"上還空着的部分,可以用來記錄新的操作。如果write pos追上checkpoint,表示"粉板"滿了,這時候不能再執行新的更新,得停下來先擦掉一些記錄,把checkpoint推進一下。
有了redo log,InnoDB就可以保證即使數據庫發生異常重啟,之前提交的記錄都不會丟失,這個能力稱為crash-safe
Binlog 邏輯日志
是service層日志模塊
所有引擎都可以用
Binlog以追加的方式記錄邏輯操作 是由執行器生成的
為什么會有兩份日志模塊?
因為最開始MySQL里並沒有InnoDB引擎。MySQL自帶的引擎是MyISAM,但是MyISAM沒有crash-safe的能力,binlog日志只能用於歸檔。而InnoDB是另一個公司以插件形式引入MySQL的,既然只依靠binlog是沒有crash-safe能力的,所以InnoDB使用另外一套日志系統——也就是redo log來實現crash-safe能力
兩種日志特點對比:
-
redo log是InnoDB引擎特有的;binlog是MySQL的Server層實現的,所有引擎都可以使用。
-
redo log是物理日志,記錄的是"在某個數據頁上做了什么修改";binlog是邏輯日志,記錄的是這個語句的原始邏輯,比如"給ID=2這一行的c字段加1 "。
-
redo log是循環寫的,空間固定會用完;binlog是可以追加寫入的。"追加寫"是指binlog文件寫到一定大小后會切換到下一個,並不會覆蓋以前的日志
執行器和innodb在執行數據更新的內部流程
-
執行器先找引擎取ID=2這一行。ID是主鍵,引擎直接用樹搜索找到這一行。如果ID=2這一行所在的數據頁本來就在內存中,就直接返回給執行器;否則,需要先從磁盤讀入內存,然后再返回。
-
執行器拿到引擎給的行數據,把這個值加上1,比如原來是N,現在就是N+1,得到新的一行數據,再調用引擎接口寫入這行新數據。
-
引擎將這行新數據更新到內存中,同時將這個更新操作記錄到redo log里面,此時redo log處於prepare狀態。然后告知執行器執行完成了,隨時可以提交事務。
-
執行器生成這個操作的binlog,並把binlog寫入磁盤。
-
執行器調用引擎的提交事務接口,引擎把剛剛寫入的redo log改成提交(commit)狀態,更新完成。
執行流程圖,
圖中淺色框表示是在InnoDB內部執行的,
深色框表示是在執行器中執行的
為什么日志需要"兩階段提交?
重點:
Binlog 的完成在redolog 的prepare和commit之間
在binlog之前 redolog已經准備好在內存中,但是未寫入磁盤
在binlog之后 redolog 才處於提交狀態准備寫入磁盤。
Redolog 和binlog是兩個獨立的階段,並不依賴
即,數據已經在內存中修改完畢,修改數據的操作也記錄完了,
但是數據庫的磁盤文件還沒有真正寫入。
假設當前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用於保證crash-safe能力。innodb_flush_log_at_trx_commit這個參數設置成1的時候,表示每次事務的redo log都直接持久化到磁盤。這個參數建議你設置成1,這樣可以保證MySQL異常重啟之后數據不丟失。
sync_binlog這個參數設置成1的時候,表示每次事務的binlog都持久化到磁盤。這個參數建議你設置成1,這樣可以保證MySQL異常重啟之后binlog不丟失。