作爲一個後端工程師,想必沒有人沒用過數據庫,跟我一起復習一下MySQL吧,本文是我學習《MySQL實戰45講》的總結筆記的第一篇,總結了MySQL的基礎架構、一個查詢語句的執行過程 以及 一條更新語句的執行過程。
1 MySQL的基礎架構
爲了窺其全貌,我們首先需要了解一下MySQL的基礎架構,如下圖所示:
圖片來自林曉斌《MySQL實戰45講》
可以從圖中看到,MySQL分爲了Server層和存儲引擎層,Server層包括了連接器(用於管理連接和權限驗證)、查詢緩存(用於緩存查詢結果)、分析器(詞法分析和語法分析)、優化器(執行計劃生成及索引選擇)以及執行器(操作存儲引擎接口,返回最終結果),而存儲引擎層則負責數據的存儲和提取。存儲引擎層是插件式架構,目前MySQL默認存儲引擎是InnoDB。當然,你也可以切換爲MyISAM或Memory。
2 一個查詢語句的執行過程
現在我們來看一個查詢語句的執行過程,會涉及到剛剛提到的基礎架構中的所有組件。查詢語句如下:
select * from T where ID=10;
(1)連接器
首先,我們需要通過以下語句連接到這個數據庫上,這時候我們使用的就是連接器這個組件來創建和MySQL的連接。
mysql -h$ip -P$port -u$user -p
建立連接的過程通常是比較複雜的,建議儘量使用長連接!
長連接累計過多也可能會導致內存佔用問題,解決方案有以下兩種:
-
定期斷開長連接
-
通過執行mysql_reset_connection來重新初始化連接資源(MySQL 5.7+)
(2)查詢緩存
查詢緩存顧名思義就是對查詢結果的緩存,它會直接存放在內存中。但是,查詢緩存的失效非常頻繁,弊大於利。因此,它只適用於靜態配置表之類的場景,而MySQL新版本(8.0)直接將其廢棄了。
建議對於查詢緩存按需使用,設置參數:query_cache_type=DEMAND。對於確定使用緩存的時候,可以直接通過以下語句查詢:
select SQL_CACHE * from T where ID=10;
(3)分析器
對於不走緩存的命令,就要開始執行詞法分析和語法分析了。
詞法分析,就是從這一行語句中識別出:select是查詢關鍵詞、字符串T是表名T,字符串ID是列ID,以此類推。
語法分析,就是根據語法規則判斷語句是否滿足MySQL語法,如果語句不對,則會拋出“You have an error in your SQL syntax”的錯誤提示。
(4)優化器
正式執行之前,優化器會對你的語句進行一些優化,比如:
-
在表中含有多個索引的時候,決定使用哪個索引。
-
在一個語句中有多表關聯(join)的時候,決定各個表的連接順序。
(5)執行器
前期準備工作就緒之後,正式開始執行。
首先,判斷用戶是否對此表具有執行查詢的權限。
其次,根據表的引擎定義,使用這個引擎提供的接口。
對於這個語句而言,具體步驟如下:
-
調用InnoDB引擎接口取這個表的第一行,判斷ID值是不是10,是則將這行存在結果集中。
-
調用引擎接口取“下一行”,重複相同的邏輯判斷。
-
將滿足條件的記錄集作爲結果集返回給客戶端。
到此,這個語句就執行完成了。
最終,我們可以通過下圖所示的流程來回顧一下這個查詢語句的執行過程全貌。
綜述,對於一個查詢語句的執行過程有了初步瞭解,實際就是對MySQL邏輯架構有了一個初步印象。
3 一個更新語句的執行過程
現在我們來看看如下所示的一條更新語句的執行過程:
update T set c=c+1 where ID=2;
事實上,更新語句和查詢語句所經歷的流程一模一樣,不同的地方在於更新語句在執行器階段還會涉及到兩個日誌模塊:redo log(重做日誌)和 binlog(歸檔日誌)。
redo log
redo log即重做日誌,它是InnoDB引擎特有的日誌。
當有一條記錄需要更新時,InnoDB引擎會將其記錄(記錄內容:在某個數據頁上做了什麼修改)寫到redo log中,並更新內存。然後,InnoDB引擎會在適當的時候將這個操作記錄更新到磁盤裏面。
redolog是循環寫入的方式,空間固定會用完,因此當空間滿了的話,需要提前擦除一些數據騰空間。
有了redolog,InnoDB引擎纔可以說具有了crash-safe的能力。
binlog
binlog即歸檔日誌,它是MySQL Server層實現的,所有引擎(包括InnoDB)均可使用。
binlog屬於邏輯日誌,記錄的內容類似於“給 ID=2 這一行的 c 字段加 1”這種。與redolog不同,binlog是通過“追加寫”的形式記錄的。
binlog在MySQL主從備份的場景中使用較多,如果我們需要從數據庫同步主數據庫的內容,我們就可以通過binlog來進行同步。而阿里的開源項目Canal則是一個針對binlog消費的經典案例:
那麼,問題來了:爲什麼有兩套日誌?
因爲早期MySQL的默認引擎是MyISAM,那時還沒有InnoDB,而binlog只能用於歸檔,因此不具備crash-safe的能力。因此,後來InnoDB自己通過redo log實現了crash-safe能力。
兩階段提交
對開頭提到的這個更新語句的執行過程如下圖所示:
圖片來自林曉斌《MySQL實戰45講》
我們可以看到,引擎會將新數據先更新到內存中,同時將其操作記錄到redo log中,這時redo log處於prepare狀態。
然後,告知執行器執行完成,時刻準備着提交事務。
執行器收到通知,開始生成這個更新操作的binlog,並將其寫入磁盤。
最後,執行器調用引擎提供的提交事務的接口,這時引擎會將寫入的redo log改成commit狀態,代表更新操作正式結束。
這就是MySQL的兩階段提交,prepare和commit兩個狀態。
那麼,問題來了:爲什麼要兩階段提交?
因爲redo log 和 binlog 都可以用於表示事務的提交狀態,而兩階段提交就是讓這兩個狀態保持邏輯上的一致。兩階段提交也是跨系統維持數據邏輯一致性的一個常見方案。
4 小結
本文總結了MySQL的基礎架構、一條查詢語句和一條更新語句的執行過程,通過高屋建瓴的視角俯視了MySQL最常見的應用場景,可以幫助我們有一個初步的瞭解。
參考資料
林曉斌(丁奇),《MySQL實戰45講》
👇掃碼訂閱《MySQL實戰45講》