1. 程式人生 > 資料庫 >MySQL45講——日誌系統:一條SQL更新語句是如何執行的 學習筆記

MySQL45講——日誌系統:一條SQL更新語句是如何執行的 學習筆記

資料庫備份:MySQL可以恢復到半個月內任意一秒的狀態。
MySQL查詢語句和更新語句的異同?
在這裡插入圖片描述
查詢語句的執行流程更新語句也會走一遍
執行語句前要先連線資料庫,這是聯結器的工作。
在一個表有更新時,跟這個表有關的查詢快取會失效,更新語句會把表上的所有快取結果清空。因此,不建議使用查詢快取
分析器會通過詞法和語法解析知道這是一條更新語句。
優化器決定使用什麼索引。
執行器負責具體執行。
與查詢流程不同的是,更新流程還涉及兩個重要的日誌模組:redo log和binlog(歸檔日誌)。

redo log
比喻:賒賬的小黑板 & 賬簿
賒賬記錄的兩種方式:
①直接把賬本翻出來,把這次賒的賬加上去或扣掉;

②先在小黑板上記下這次的賬,有空時再把賬本翻出來核算。
方式一操作麻煩,效率太低,在賬本上找名字很耗時,同時還需要計算、修改。方式二更加便利。
MySQL的執行一條更新語句就像一次賒賬/銷賬過程。
每次更新操作都要寫進磁碟就像直接在賬本上修改(方式一),寫入磁碟本身IO成本高,磁碟也要找到對應的那條記錄(查詢成本很高),然後再更新。由此看,如果每次更新操作都寫進磁碟,IO成本和查詢成本都很高。MySQL設計者採用方式二來提升效率,引入redo log作為小黑板。先將更新語句寫入日誌,等不忙的時候再將更新語句寫入磁碟、查詢、更新。上述過程就是MySQL常說的WAL(write-ahead logging)技術

個人的理解:就像在小黑板上記錄沒有真正完成賒賬/銷賬(因為沒有歷史額,所以只是記錄),在賬本上查詢、修改才真正完成賒賬/銷賬。將更新語句寫入redo log並沒有執行該語句,將其寫入磁碟,查詢到對應的記錄,然後更新才是執行該更新語句。 是在執行完更新操作之後,才把更新語句寫入redo log的。

如果出現某天賒賬/銷賬的特別多,小黑板寫滿了,怎麼辦呢?
掌櫃只能先放下手中的活兒,把小黑板中的一部分賒賬記錄更新到賬本中。然後把這些記錄從粉板上擦掉,為記新賬騰出空間。
小黑板的大小有限,redo log也是固定大小,容量有限的。從頭開始寫,寫到末尾就又回到開頭迴圈寫。
在這裡插入圖片描述
write pos是當前記錄的位置,一邊寫一邊後移,寫到末尾後又回到開頭。checkpoint是當前要擦除的位置,也是往後推移並迴圈的,插除記錄前要將資料更新到磁碟。write pos和checkpoint之間的是小黑板上空著的部分,可以用來記錄新的操作。如果write pos追上checkpoint,表示小黑板滿了,得停下來將一些記錄寫入磁碟,然後擦掉已經寫入磁碟的記錄,把checkpoint推進一下。

只要賒賬記錄記在了小黑板上或者賬本上了,之後即使掌櫃忘記了,比如突然停業幾天,恢復生意後仍然可以通過賬本和小黑板上的資料明確賒賬賬目。類似的,有了redo log和磁碟的結合使用,即使資料庫發生異常重啟,之前提交的記錄都不會丟失,這個能力稱為crash-safe

binlog
redo log是InnoDB引擎特有的日誌。
binlog是server層的日誌。
看看執行器和InnoDB引擎在執行下面的update語句時的內部流程:

建立表T:

create table T(ID int primary key,c int);

執行一條更新語句:

update T set c=c+1 where ID=2;

①執行器獲取要修改的行:執行器先找引擎取ID=2這一行(如何找:ID是主鍵,引擎直接到主鍵索引樹上搜索找到這一行)。如果ID=2這一行所在的資料頁本來就在記憶體中,就直接返回給執行器;否則,需要先從磁碟讀入記憶體,然後再返回。
②執行器拿到引擎給的行資料,把這個值加上1,得到新的一行資料,再呼叫引擎介面寫入這行新資料。
③引擎將這行新資料更新到記憶體中,同時將這個更新操作記錄到redo log裡面,此時redo log處於prepare狀態。然後告知執行器執行完成了,隨時可以提交事務。
④執行器生成這個操作的binlog,並把binlog寫入磁碟。
⑤執行器呼叫引擎的提交事務介面,引擎把剛剛寫入的redo log改成commit狀態,更新完成。
下面是update語句的執行流程圖,淺色框表示在InnoDB引擎內部執行的,深色框表示是在執行器中執行的。
在這裡插入圖片描述

上述過程將redo log的寫入拆成了兩個步驟:prepare和commit。兩階段提交:為了保證redo log和binlog邏輯上的一致。

小結:
redo log是物理日誌,binlog是邏輯日誌。
redo log保證crash safe的能力。
將innodb_flush_at_trx_commit這個引數設定成1,表示每次事務的redo log都直接持久化到磁碟,可以保證MySQL異常重啟後資料不丟失。
將sync_binlog這個引數設定為1,表示每次事務的binlog都持久化到磁碟,可以保證MySQL異常重啟後binlog不丟失。
兩階段提交是跨系統維持資料庫邏輯一致性時常用的一個方案。