假如說咱們有一張表 T
,表裏只有一個字段 ID
,當咱們執行下邊這條SQL語句時:mysql
mysql> select * fron T where ID=10;
複製代碼
在咱們眼中能看到的只是輸入一條 SQL語句,返回一條查詢結果,卻未曾知道這條SQL在MySQL的內部經歷了什麼,下面咱們來一步一步的分析一下;以下是MySQL的基本架構圖,從圖中能夠清楚的看到SQL在MySQL中各個功能模塊執行的過程: sql
大致來講,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 並非徹底相同的。