MySQL 邏輯架構
從整體架構切入MySQL的學習,可以對 MySQL 有一個整體的把握,鳥瞰全貌,不至于一下陷入細節(jié)里邊, 對于以后深入理解 MySQL 也是有很大幫助的。本文通過講解一條查詢語句在 MySQL 內部的執(zhí)行過程,讓您對 MySQL 邏輯架構有更深的了解。
1. 邏輯架構組成
上方是 MySQL 邏輯架構的簡單示意圖,總的來說,MySQL 包含 Server 層和存儲引擎層兩大部分。
Server 層包括連接池、查詢緩存、解析器、優(yōu)化器、執(zhí)行器等,MySQL 的核心服務都在這一層。
存儲引擎層負責數(shù)據(jù)的存儲和提取,MySQL 采用插件式的存儲引擎,常見的存儲引擎有 InnoDB、MyISAM、 CSV 等。其中 InnoDB 是最常用的存儲引擎,也是 MySQL 的默認存儲引擎(從5.5.5版本開始)。
2. 一條查詢語句是如何執(zhí)行的
上一小節(jié)介紹了 MySQL 的邏輯架構組成,那么各模塊之間是如何協(xié)同工作的呢?這里以一條最簡單的查詢語句為例子,我們一起來看看這條語句在 MySQL 內部是如何執(zhí)行的。
select id from a where id=1;
2.1 連接池
首先,我們通過 mysql 這個客戶端工具進行數(shù)據(jù)庫的連接,這時遇到的是連接池。連接池負責客戶端的連接管理、授權認證。
連接命令如下(輸入完連接命令后,需要輸入用戶密碼):
mysql -h localhost -u root -p
-
如果用戶名和密碼認證通過,連接池會通過權限表獲取這個用戶名所擁有的權限信息;
-
如果用戶名或密碼認證不通過,則會收到一個錯誤提示:“ERROR 1045 (28000): Access denied for user ‘root’@‘localhost’”;
建立連接后,會產生相應的連接信息,可以通過 show processlist 命令查看。下方圖中 Id 為 5048 這一行,即為成功連接數(shù)據(jù)庫所建立的連接信息,請注意 Command 這一列,值為”Sleep“,表明這是一個空閑連接。成功連接數(shù)據(jù)庫后,如果沒有任何動作,這個連接就會變成空閑狀態(tài)。
root@localhost [(none)]>show processlist;
+------+------+-----------+------+---------+------+----------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+------+------+-----------+------+---------+------+----------+------------------+
| 4771 | root | localhost | NULL | Query | 0 | starting | show processlist |
| 5048 | root | localhost | NULL | Sleep | 3 | | NULL |
+------+------+-----------+------+---------+------+----------+------------------+
2 rows in set (0.00 sec)
2.2 查詢緩存
連接成功建立后,來到第二步查詢緩存。查詢緩存負責將執(zhí)行過的語句和結果緩存在內存中。
在獲取一個查詢請求后,MySQL會先到查詢緩存進行查看
- 如果select語句在查詢緩存中能夠找到,則直接返回結果給客戶端,跳過解析、優(yōu)化、執(zhí)行階段。
- 如果select語句沒能在查詢緩存中找到,則繼續(xù)后面的解析、優(yōu)化、執(zhí)行階段。
從這里可以看到,如果命中查詢緩存,MySQL 會直接返回結果給客戶端,后面的一系列操作不需要再執(zhí)行,是非常高效的。但實際情況并非如此,查詢緩存非常容易失效。
因為只要一個表有更新操作,那這個表所有的查詢緩存都會被清空。對一個承載正常業(yè)務的數(shù)據(jù)庫來說,更新操作是非常頻繁的,這就意味著查詢緩存經常失效,從而導致查詢緩存的命中率非常低。所以,使用查詢緩存反而會給數(shù)據(jù)庫帶來額外的負擔,在實際生產環(huán)境中,我們建議關閉查詢緩存。
關閉查詢緩存的方法有兩種:
-
臨時:在 MySQL 中直接用命令行執(zhí)行;
set global query_cache_size=0 set global query_cache_type=0
-
永久:將以下兩個參數(shù)添加至配置文件 my.cnf,并重啟 MySQL;
query_cache_type=0 query_cache_size=0
2.3 解析器
如果沒有命中查詢緩存,接下來就要進入解析器階段了。解析器負責詞法解析和語法解析。
首先是詞法解析,MySQL 需要識別所輸入的字符串分別代表什么,它會從左到右一個字符、一個字符地輸入,然后根據(jù)構詞規(guī)則識別單詞。
select id from a where id=1;
關鍵字 | 非關鍵字 | 關鍵字 | 非關鍵字 | 關鍵字 | 非關鍵字 |
---|---|---|---|---|---|
select | 字段id | from | 表名a | where | 字段id等于1 |
接下來是語法解析,判斷輸入的這個SQL語句是否符合MySQL語法規(guī)則。如果語法不對,會收到錯誤信息提示:“ERROR 1064 (42000): You have an error in your SQL syntax;”。如下面這個SQL語句的where少了一個e。
root@localhost [tempdb]>select id from a wher id=1;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'id=1' at line 1
2.4 優(yōu)化器
從解析器出來,就到了優(yōu)化器階段。優(yōu)化器負責找到最優(yōu)的執(zhí)行計劃,也就是決定SQL語句的執(zhí)行方案。
一條查詢可以有很多種執(zhí)行方式,最后都返回相同的結果 。比如下面這個查詢SQL,查詢表a中字段id等于1的值
select id from a where id=1;
- 可以遍歷表a所有行,找出所有id等于1的值
- 也可以通過索引idx_id,找到id等于1的值。當然,前提是字段id有創(chuàng)建索引idx_id。
兩種方案的結果是一樣的,但是執(zhí)行效率不一樣,優(yōu)化器的作用就是選擇最優(yōu)的執(zhí)行方案。
2.5 執(zhí)行器
現(xiàn)在到了執(zhí)行SQL語句的階段,也就是執(zhí)行器。執(zhí)行器負責調用存儲引擎,拿到查詢結果。
select id from a where id=1;
假設這個例子中的字段id沒有索引,執(zhí)行器的流程大致如下:
- 調用 InnoDB 引擎接口獲取表 a 的第一行,如果 id 值等于 1,則將 id 值存進結果集,如果 id 值不等于 1,則跳過,取下一行;
- 調用 InnoDB 引擎接口獲取下一行,重復第一步的邏輯,一直到表 a 的最后一行;
- 將符合查詢條件的結果集返回給客戶端。
3. 小結
一條 SQL 查詢的過程,大致就是這樣的流程:連接池、查詢緩存、解析器、優(yōu)化器、執(zhí)行器。
連接池負責連接管理,查詢緩存建議關閉,解析器讓 MySQL 知道要做什么,優(yōu)化器讓 MySQL 知道怎么做,執(zhí)行器負責執(zhí)行取數(shù)。
通過本文的講解,對您認識 MySQL 邏輯架構,有所幫助嗎?歡迎反饋寶貴意見,以便我們不斷改進。