當執行一條查詢語句時,MySQL內部經歷了什麼?

假如說咱們有一張表 T ,表裏只有一個字段 ID,當咱們執行下邊這條SQL語句時:mysql

mysql> select * fron T where ID=10;
複製代碼

在咱們眼中能看到的只是輸入一條 SQL語句,返回一條查詢結果,卻未曾知道這條SQL在MySQL的內部經歷了什麼,下面咱們來一步一步的分析一下;以下是MySQL的基本架構圖,從圖中能夠清楚的看到SQL在MySQL中各個功能模塊執行的過程: sql

MySQL邏輯架構圖

大致來講,MySQL能夠分爲Server層存儲引擎兩部分。 Server層:包括鏈接器、分析器、查詢緩存、優化器、執行器等。 存儲引擎:負責數據的存儲和提取。其架構模式是插件式的,支持InnoDB、MyISAM、Memory等多種存儲模式。現現在最經常使用的存儲引擎是InnoDB,它從MySQL5.5.5開始成爲了默認存儲引擎。也就是說當咱們不指定存儲引擎時默認使用的就是InnoDB,咱們也能夠在 create table時經過engine=memory 來指定存儲引擎。數據庫

從圖中能夠看出:不一樣的存儲引擎共用同一個 server層,也就是鏈接器到執行器那一部分。緩存

如今咱們跟着開篇提到的那條SQL進入MySQL內部究竟是怎麼執行的,Let's go:服務器

鏈接器

首先,咱們須要鏈接上數據庫,這時候接待咱們的就是鏈接器,鏈接器主要負責的工做就是跟客戶端創建鏈接、獲取權限、維持和管理鏈接。鏈接命令以下:架構

-- $ip: 服務器IP
-- $port: MySQL端口號
-- $user: 用戶名
mysql -h$ip -P$port -u$user -p
複製代碼

輸入完命令後,咱們須要在交互界面輸入密碼,雖然密碼也能夠在 -p 後面,卸載命令行中,可是這樣可能會致使密碼泄露,不建議這麼作。優化

  • 若是用戶名或密碼不對,則會收到一個「Access denied for user」錯誤,而後客戶端程序結束執行。
  • 若是用戶名密碼認證經過,鏈接器則會去權限表中查詢該用戶所擁有的權限,這個連接裏邊的權限邏輯判斷,全都依賴於此時讀到的權限。

這就意味着,當一個用戶成功創建鏈接後,即便使用管理員帳戶對其權限作了修改,也不會當即生效,只有從新創建連接後纔會使用新的權限設置。spa

查詢緩存

鏈接創建成功後,咱們就能夠執行SQL語句了,這時候會先來查詢緩存:插件

MySQL拿到一個SQL語句以後會先到緩存看看是否在此以前執行過這條語句,以前執行的語句可能會以 key-value 的形式直接緩存在內存中。key是查詢語句,value是查詢結果。若是你的查詢能在緩存中找到相應的key,則直接返回其對應的value 給客戶端。命令行

若是語句不在查詢緩存中,就會繼續後面的執行階段。執行完成後,執行結果會被存入查詢緩存中。你能夠看到,若是查詢命中緩存,MySQL不須要執行後面的複雜操做,就能夠直接返回結果,這個效率會很高。

可是一樣的緩存的弊端也很大,那是由於緩存失效的頻率很是頻繁,只有對一個表的更新操做,這個表上的全部查詢緩存都會清空。所以極可能咱們費老勁把它們給存起來,還沒來得及用,就被一個更新把緩存全都給清空了,一下回到解放前。所以對於更新壓力大的數據庫來說,並不建議使用緩存;若是在業務中有一張靜態表,很長時間纔回去更新一次,那麼使用緩存纔是合適 。

MySQL 提供了一種方式設置默認不使用緩存:將參數query_cache_type設置爲DEMAND。這樣對於默認的SQL語句則不使用緩存,若是要使用緩存的話可使用SQL_CACHE顯示的指定,以下:

mysql> select SQL_CACHE * from T where ID=10;
複製代碼

Tips:MySQL 8.0 版本直接將查詢緩存的整塊功能刪掉了,也就是說 8.0 開始完全沒有這個功能了。

分析器

若是沒有命中緩存的話,則真正開始執行語句了;首先MySQL要知道咱們要作什麼,因此要先對SQL進行解析。

分析器首先進行詞法分析。咱們輸入的SQL是由多個字符串組成的,MySQL須要識別出來裏邊的字符串分別是什麼,表明什麼意思。

MySQL從咱們輸入的select識別出來這是一個查詢語句,它還須要把字符串T識別成表名T,把ID識別成列ID

詞法分析結束以後,須要對SQL進行語法分析。根據詞法分析的結果,語法分析器會根據語法規則來判斷咱們輸入的這條SQL是否知足MySQL的語法規則。 若是咱們的語法不對的話,咱們會受到MySQL的錯誤提示You have an error in your SQL syntax,好比這條SQL中的select少了個s

mysql> elect * from t where 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 'elect * from t where ID=1' at line 1
複製代碼

通常語法錯誤會提示第一個出現錯誤的位置,因此你要關注的是緊接user near的內容。

優化器

通過了分析器MySQL知道咱們要作什麼了,在它開始執行以前,還要先通過優化器的處理。

優化器是在表裏面有多個索引的時候,決定使用哪一個索引;或者在一個語句有多表關聯(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

這兩種執行方式的邏輯和結果都是同樣的,可是執行效率會有所不一樣,優化器的做用就是決定選擇哪一種方案。

當優化器階段完成後,這個語句的整個執行方案就已經肯定下來了,接下來就是進入執行器階段。

執行器

MySQL經過分析器知道了咱們要作什麼,經過優化器知道了應該怎麼作,因而就進入執行器階段,開始真正的執行語句。

開始執行的時候,首先要確認咱們是否有操做這個表(T)的權限,若是沒有權限則會返回沒有權限的錯誤;

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,若是不是則跳過,若是是則將這行存在結果集中。
  • 調用引擎接口取下一行,重複相同的邏輯判斷,直到取到這個表的最後一行。
  • 執行器將上述遍歷過程的全部知足條件的行組成的記錄集做爲結果集返回給客戶端。

至此,這個語句就執行完成了。

對於有索引的表,執行的邏輯也差很少。第一次調用的是「取知足條件的第一行」這個接口,以後循環取「知足條件的下一行」這個接口,這些接口都是引擎中已經定義好的。你會在數據庫的慢查詢日誌中看到一個 rows_examined 的字段,表示這個語句執行過程當中掃描了多少行。這個值就是在執行器每次調用引擎獲取數據行的時候累加的。

在有些場景下,執行器調用一次,在引擎內部則掃描了多行,所以引擎掃描行數跟 rows_examined 並非徹底相同的。

相關文章
相關標籤/搜索