深入學習MySQL,從概覽MySQL邏輯架構開始,
首先來看一下MySQL的邏輯架構圖:

MySQL邏輯架構大概可以分為三層:
- 客戶端:最上層的服務并不是MySQL所獨有的,大多數基于網路的客戶端/服務器的工具或者服務都有類似的架構,比如連接處理、授權認證、安全等等,
- Server層:大多數MySQL的核心服務功能都在這一層,包括查詢決議、分析、優化、快取以及所有的內置函式(例如,日期、時間、數學和加密函式),所有跨存盤引擎的功能都在這一層實作:存盤程序、觸發器、視圖等,
- 存盤引擎層:第三層包含了存盤引擎,存盤引擎負責MySQL中資料的存盤和提取,Server層通過API與存盤引擎進行通信,這些介面屏蔽了不同存盤引擎之間的差異,使得這些差異對上層的查詢程序透明,
值得一提的是在MySQL8.0中取消了查詢快取,大概的理由是查詢快取存在嚴重的可伸縮性問題,并且很容易成為嚴重的瓶頸快取,將快取移動到客戶端能識訓更好的性能,

通過一條查詢陳述句的執行程序,來了解一些關鍵的部件:
mysql> select * from T where ID=10;
1、連接器
首先,需要連接資料庫,
當客戶端(應用)連接到MySQL服務器時,服務器需要對其進行認證,認證基于用戶名、原始主機資訊和密碼,
連接命令:
mysql -h$ip -P$port -u$user -p
除了基本認證之外,連接器還會進行一些執行緒的處理,
每個客戶端連接都會在服務器行程中擁有一個執行緒,這個連接的查詢只會在這個單獨的執行緒中執行,該執行緒只能輪流在某個CPU核心或者CPU中運行,服務器會負責快取執行緒,因此不需要為每一個新建的連接創建或者銷毀執行緒,
2、查詢快取
對于SELECT陳述句,在決議查詢之前,服務器會先檢查查詢快取(Query Cache),如果能夠在其中找到對應的查詢,服務器就不必再執行查詢決議、優化和執行的整個程序,而是直接回傳查詢快取中的結果集,
但不推薦使用查詢快取,為什么呢?因為查詢快取往往弊大于利,
查詢快取的失效非常頻繁,只要有對一個表的更新,這個表上所有的查詢快取都會被清空,對于更新壓力大的資料庫來說,查詢快取的命中率會非常低,除非你的業務就是有一張靜態表,很長時間才會更新一次,比如,一個系統配置表,那這張表上的查詢才適合使用查詢快取,
好在MySQL也提供了這種“按需使用”的方式,可以將引數query_cache_type設定成DEMAND,這樣對于默認的SQL陳述句都不使用查詢快取,而對于確定要使用查詢快取的陳述句,可以用SQL_CACHE顯式指定,如下:
mysql> select SQL_CACHE * from T where ID=10;
上面也提到了MySQL8.0徹底廢棄了查詢快取的功能,
3、決議器
如果快取沒有命中的話,MySQL會對查詢陳述句進行決議,簡單說決議的作用將我們人能看懂的SQL決議成MySQ能識別的語言,
決議器先會做“詞法決議”,輸入的是由多個字串和空格組成的一條SQL陳述句,MySQL需要識別出里面的字串分別是什么,代表什么,
MySQL從輸入的"select"這個關鍵字識別出來,這是一個查詢陳述句,它也要把字串“T”識別成“表名T”,把字串“ID”識別成“列ID”,
做完了這些識別以后,就要做“語法決議”,根據詞法決議的結果,語法決議器會根據語法規則,判斷輸入的這個SQL陳述句是否滿足MySQL語法,
4、優化器
經過了決議器器,MySQL知道我們要干什么,
接下來并不是直接執行,而是會在優化器這一層進行優化,優化器是個非常復雜的部件,它會幫我去使用他自己認為的最好的方式去優化這條 SQL 陳述句,并生成一條條的執行計劃,
例如在表里面有多個索引的時候,決定使用哪個索引;或者在一個陳述句有多表關聯(join)的時候,決定各個表的連接順序,比如你執行下面這樣的陳述句,這個陳述句是執行兩個表的join:
mysql> select * from t1 join t2 using(ID) where t1.c=10 and t2.d=20;
- 既可以先從表t1里面取出c=10的記錄的ID值,再根據ID值關聯到表t2,再判斷t2里面d的值是否等于20,
- 也可以先從表t2里面取出d=20的記錄的ID值,再根據ID值關聯到t1,再判斷t1里面c的值是否等于10,
這兩種執行方法的邏輯結果是一樣的,但是執行的效率會有不同,而優化器的作用就是決定選擇使用哪一個方案,
優化器階段完成后,這個陳述句的執行方案就確定下來了,然后進入執行器階段,如果你還有一些疑問,比如優化器是怎么選擇索引的,有沒有可能選擇錯等等,沒關系,我會在后面的文章中單獨展開說明優化器的內容,
5、執行器
MySQL通過決議器知道了你要做什么,通過優化器知道了該怎么做,于是就進入了執行器階段,執行器會根據一系列的執行計劃去呼叫存盤引擎的介面去完成SQL的執行,
開始執行的時候,要先判斷一下你對這個表T有沒有執行查詢的權限,如果沒有,就會回傳沒有權限的錯誤,如下所示(在工程實作上,如果命中查詢快取,會在查詢快取放回結果的時候,做權限驗證,查詢也會在優化器之前呼叫precheck驗證權限),
mysql> select * from T where ID=10;
ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'
如果有權限,就打開表繼續執行,打開表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的介面,
比如我們這個例子中的表T中,ID欄位沒有索引,那么執行器的執行流程是這樣的:
- 呼叫InnoDB引擎介面取這個表的第一行,判斷ID值是不是10,如果不是則跳過,如果是則將這行存在結果集中;
- 呼叫引擎介面取“下一行”,重復相同的判斷邏輯,直到取到這個表的最后一行,
- 執行器將上述遍歷程序中所有滿足條件的行組成的記錄集作為結果集回傳給客戶端,
至此,這個陳述句就執行完成了,
對于有索引的表,執行的邏輯也差不多,第一次呼叫的是“取滿足條件的第一行”這個介面,之后回圈取“滿足條件的下一行”這個介面,這些介面都是引擎中已經定義好的,
參考:
【1】:《高性能MySQL》
【2】:極客時間 《MySQL實戰45講》
【3】:《MySQL技術內幕 InnoDB存盤引擎》
【4】:MySQL 8.0: Retiring Support for the Query Cache
【5】:頭條二面: 詳解一條 SQL 的執行程序
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/275773.html
標籤:其他
