02 | 日誌系統:一條SQL更新語句是如何執行的?
#<-- 該文章借鑑林曉斌老師mysql實戰45講 ->
一條SQL更新語句是如何執行的?
-
更新是建立在查詢之上的,可以說更新==查詢+修改,所以更新操作也會走一遍查詢語句的流程.
-
mysql的更新操作涉及到兩個重要日誌檔案:redo log(重做日誌)和 binlog(歸檔日誌):
a. redo log 是屬於innodb引擎的,myisam引擎不存在redo log 所以這也是為啥innodb擁有事務功能,而myisam沒有! 當一條記錄需要更新的時候,innodb引擎會先把記錄寫到redo log中,並更新記憶體,這個時候對客戶端來說更新操作就算是完成了,innodb引擎會在系統空閒的時候將redo log中的操作更新到磁碟中
(注意:雖然redo log 和 bin log,為了快速響應SQL充當了粉板,不過它們本身也是檔案,在進行日誌記錄的時候也需要讀寫磁碟不過日誌檔案是順序寫,不需要定址,而更新資料需要定址,所以寫日誌效率更高)b. redo log 的檔案個數和大小是可以配置的,並且記憶體空間可以迴圈利用,存在於data目錄中,以ib_logfile[number]方式命名,例如 ib_logfile[0]
c. binlog屬於服務層,只能用於歸檔,binlog是二進位制格式,形式為mysql-bin.000001, mysql-bin.000001 等
-
兩種日誌的比較:
a. redo log 是innodb特有的,二binlog屬於Server層,所有引擎都可以使用b. redo log是物理日誌,記錄的是“在某個資料頁上做了什麼修改”,有了redo log 即使資料庫異常重啟,之前提交的資料也不會丟失,這個能力成為crash-safe;binlog是邏輯日誌,記錄的是這個語句的原始邏輯 ,假如你需要將資料庫恢復到一週前的狀態就需要用到binlog了 ;binlog有兩種模式,statement 格式的話是記sql語句, row格式會記錄行的內容,記兩條,更新前和更新後都有;
c. redo log是迴圈寫的,例如配置redo log 數量為兩個大小為1G,當第二個寫完的時候innodb引擎會將第一個日誌檔案部門空間的日誌寫入磁碟並擦除,以供迴圈使用
-
update student set name=‘zhangqi’ where id=2的執行流程:
a. 執行器先找儲存引擎取id為2的記錄. 如果id=2的記錄所存在的資料頁剛好在記憶體中,則直接返回給執行器,否則需要從磁碟將記錄載入到記憶體再返回給執行器;
b. 執行器拿到這條記錄後將name改為‘zhangqi’,得到一條新的記錄,然後再將新得到的記錄返回給引擎;
c. 引擎將得到的記錄更新到記憶體中,並寫入redo log中(寫入磁碟),此時redo log處於perpare狀態,引擎將此狀態通知給執行器;
d. 執行器接收引擎通知的perpare狀態後,生成更新操作的binlog,並寫入磁碟,然後再調取引擎的提交事務介面;
e. 引擎將redo log 改為commit狀態,寫入磁碟,更新完成
f. 下圖為SQL update T set c=c+1 where ID=2; 的執行流程,其中深色框在執行器中執行,淺色則在引擎中.
-
為啥redo log 分兩階段提交?
目的:確保redo log 和 binlog保持邏輯上的一致
假如你最近的一次資料庫全量備份是一週前,假如你現在需要恢復到三天前的狀態a. 先寫binlog,後寫redo log。也就是說更新語句寫完binlog的時候,資料庫異常重啟,這個時候資料庫中 id 為2 的記錄name值並沒有改為 ‘zhangqi’,當你需要恢復到 三天前的狀態時需要用到binlog,這個時候就會將 id為2的記錄name值改為 ‘zhangqi’, 而我們知道實際並沒有修改.
b. 先寫redo log,後寫binlog。 也就是說更新語句寫完redo log的時候,資料庫異常重啟,這個時候資料庫中 id 為2 的記錄name值已經改為 ‘zhangqi’,當你需要恢復到三天前的狀態時需要用到binlog,這個時候id為2的記錄name值並沒有改為 ‘zhangqi’, 而我們知道實際上已經修改.
c. 總結:當資料庫需要還原之前某一個狀態的時候,需要確保redo log 和 binlog 保持邏輯上的一致.
d. redo log 和 bin log 通過事務ID進行對應
-
Binlog如果已經接受,那麼redolog是prepare, binlog已經完整了對吧,這時候崩潰恢復過程會認可這個事務,提交掉。
-
Redo log不是記錄資料頁“更新之後的狀態”,而是記錄這個頁 “做了什麼改動”。
Binlog有兩種模式,statement 格式的話是記sql語句, row格式會記錄行的內容,記兩條,更新前和更新後都有。