MySQL基礎架構之一條SQL查詢語句是如何執行的?

前言

MySQL數據庫做爲優秀的關係型開源數據庫,受到全部人的青睞,咱們平常工做中,和其打交道的次數十分頻繁,那麼你知道咱們在寫sql的時候,MySQL內部是如何執行的嗎?接下來咱們就一塊兒探討一下吧。mysql

MySQL的架構

select * from T where ID=10; 這條sql語句爲例:
當咱們執行該查詢語句時,會收到MySQL給咱們返回的結果,那麼其在MySQL中是如何一步步執行的呢?
首先咱們瞭解一下MySQL的架構圖,看一下MySQL服務的組織結構:
MySQL架構.pngredis

如上圖所示:
MySQL大體能夠分爲 Server 層和存儲引擎層兩部分。
Server 層包括鏈接器、查詢緩存、分析器、優化器、執行器等,以及全部內置的函數(如日期、時間、數學和加密函數等),全部跨存儲引擎的功能都在這一層實現,如存儲過程、觸發器、視圖等。
存儲引擎層則負責數據的存取。架構是插件式的,支持 InnoDB、MyISAM、Memory等多個存儲引擎。MySQL 5.5.5版本以後,其默認存儲引擎爲InnoDB。
全部的存儲引擎共用一個Server層sql

預埋問題:memory 引擎和 redis 二者的區別

鏈接器

平時咱們命令行鏈接MySQL數據庫時的命令爲:
mysql -h127.0.0.1 -P6293 -uroot -p
這條命令就是走到了鏈接器去驗證用戶身份。
其中命令中的 mysql 是客戶端工具,用來和服務端創建鏈接。在完成TCP握手以後,鏈接器開始使用輸入的參數信息進行身份認證。數據庫

  • 若是用戶名或密碼不正確,會有一個 Access Denied for user 的錯誤,客戶端中斷。
  • 若是驗證經過,鏈接器會到權限表裏面查出用戶權限並存在變量中。以後該鏈接裏面的權限判斷邏輯,都將依賴於此時讀取到的權限。

此時咱們應該就會理解,爲何咱們給某個用戶修改了權限以後,已經存在的該用戶的鏈接不會變動爲新的權限。要想新的權限生效,只能新建該用戶的鏈接。api

鏈接完成後,若沒有後續的動做,該鏈接就處於空閒狀態,可經過
show processlist 命令查看全部鏈接狀態。其中 Command 列顯示爲 Sleep 表示系統中有一個空閒鏈接。緩存

客戶端若是長時間處於Sleep,鏈接器會將其自動斷開,斷開時間基於配置的 wait_timeout 參數,默認值是8小時。安全

若是鏈接斷開以後,客戶端再次發送請求的話,就會收到錯誤:Lost connection to MySQL server during query。此時,繼續執行請求就須要重連。架構

MySQL中,長鏈接是指鏈接成功後,若是客戶端持續有請求,則一直使用同一個鏈接。短鏈接則是指每次執行完很小的幾回查詢就斷開鏈接,下次查詢重建新鏈接。函數

創建鏈接的過程通常是比較複雜的,因此咱們應該儘可能使用長鏈接。
可是並非說長鏈接就沒有問題,由於MySQL執行過程當中臨時使用的內存是管理在鏈接對象中的,因此每次執行完請求,都會增長鏈接的內存佔用,這些資源一直不釋放會致使內存爆滿,被系統強行kill掉,從現象上看就是MySQL異常重啓了。工具

那麼解決這個問題的方案就須要考慮如下兩點:
一、按期斷開長鏈接,或在程序中判斷某個鏈接內存佔用過大,就將其關閉,以後使用再鏈接便可。
二、MySQL 5.7以後的版本,有一個 mysql_reset_connection 的函數能夠從新初始化鏈接資源,也就是釋放該鏈接佔用的內存。這個過程不須要重連和從新作權限驗證就會將鏈接恢復到剛剛建立完時的狀態。

查詢緩存

鏈接創建完成以後,咱們就能夠執行 select 語句了,此時就會來到執行邏輯的第二步:查詢緩存。

這一步不作過多說明,由於MySQL 8.0版本直接將查詢緩存的整塊功能刪掉了。

查詢緩存咱們每每不須要使用,由於其命中率實在是低下。對一個表就行更新操做就會使表上的全部查詢緩存清空。

分析器

分析器的執行邏輯就是,內建解析樹,對其進行詞法語法檢查,生成新的解析樹,語義檢查(字段,表是否存在)等。

首先進行詞法分析:主要是根據MySQL的關鍵字進行驗證和解析以及識別表名和列名。

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

以後進行語法分析:在詞法分析的基礎上,判斷sql語句是否合法。
若語句不正確(如字段不存在),就會收到報錯信息:
若是咱們執行這個語句:其中表T中字段k不存在,則會在分析器階段就報錯

select * from T where k=1

Unknown column ‘k’ in ‘where c...

以後會進行一個權限檢查,這也就是若是用戶沒有T表的權限,且sql中字段k頁不存在,卻報錯爲無權限的緣由。

優化器

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

簡單來講,優化器就是優化sql執行計劃。

優化器是在表裏面有多個索引的時候,決定使用哪一個索引;或者在一個語句有多表關聯的時候(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經過分析器知道了咱們要作什麼,經過優化器知道了該怎麼去作,到了執行器階段,就開始執行邏輯了。

簡單來講,執行器就是檢查權限,打開表,處理數據。

執行sql時,會判斷sql中的表以及操做涉及到的其餘表(如更新一張表觸發更新另外一個表的觸發器操做)在當前鏈接上有沒有相關權限,若沒有,則會返回權限錯誤;如有則繼續處理數據

注:並不是在執行器才執行權限檢查,事實上,在命中查詢緩存時,會在緩存返回結果時,作權限驗證。且在優化器以前也會調用 precheck 驗證權限。
mysql> select * from T where ID=10;

ERROR 1142 (42000): SELECT command denied to user 'b'@'localhost' for table 'T'

select * from T where ID=10; 這條語句,表T中的ID字段沒有索引,那麼執行的流程就是:
一、調用 InnoDB 引擎接口取表的第一行,判斷ID值是否爲10,不是則跳過,是則將該行信息存在結果集中。
二、調引擎接口繼續獲取下一行,重複第1步判斷,直至最後一行數據。
三、執行器將結果集返回給客戶端。

對於有索引的表,第一次調用的是引擎中取知足條件的第一行的接口,以後循環取知足條件的下一行。這些接口都是引擎中已經定義好的。

以後咱們能夠在數據庫的慢查詢日誌中看到一個 rows_examined 的字段,表示這個語句執行過程當中掃描了多少行。該值就是在執行器每次調用引擎獲取數據行的時候累加的。

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

在開啓查詢緩存時,此時的結果集會更新到查詢緩存中。

問題

一、在執行器階段爲何要判斷對錶是否具備執行查詢的權限,而不是在分析器去判斷?根據 MySQL 中的 information_schema 庫的存儲信息,理論上能夠在分析器階段判斷是否有權限。
有時sql語句要操做的表不僅是sql字面上的那些,好比有個觸發器,得在執行器階段才能肯定,因此優化器前判斷權限有時候不完整。

二、建立一個沒有 select 權限的用戶,執行 select * from T where k=1;(表T中無k字段),報錯信息爲:select command denied 而不是:unknown column 是否能夠說明在打開表以後才判斷讀取的列不存在?
並不能說明,爲了安全起見,返回無權限信息確定是最好的

總結

客戶端經過鏈接器鏈接到服務端,獲取到權限等信息,而後在鏈接的有效時長內(interactive_timeout 和 wait_timeout 參數控制,5.7版本會斷開自動重連)對客戶端請求進行處理。

判斷sql是select/update/delete/insert 中的哪種,如果 select 判斷查詢緩存是否開啓。(此時判斷的select是簡單判斷,其餘解析並無作,因此分析器會再次判斷並解析)
若查詢緩存開啓,則
若命中,則在數據返回時會判斷查詢權限,權限經過則返回數據。
若未命中,則進入分析器。
若未開啓直接進入分析器。

分析器進行詞法分析,語法分析,檢查sql的語法順序等生成解析樹,而後預處理器對解析樹進一步分析,驗證對錶是否具備相應權限以及數據表,字段是否存在,驗證經過後更新解析樹,交給優化器處理。

優化器對sql的執行計劃進行最優選擇,將最優執行計劃交給執行器。

執行器再次驗證相應權限,驗證經過後,調用引擎接口獲取數據並將結果返回給客戶端,若查詢緩存開啓,則更新緩存。如果update/delete/insert請求,則刪除查詢緩存。

一些容易漏掉的知識:
一、鏈接器是從權限表裏面查詢用戶權限並保存在一個變量裏面以供查詢緩存、分析器、執行器在檢查權限的時候使用。
二、sql在執行的過程當中,可能會有觸發器這種在運行時才能肯定是否有相應權限的操做,分析器工做結束後的 precheck 操做是不能對這種運行時涉及到的表進行權限校驗的,因此須要在執行器階段進行權限檢查。另外也是由於有precheck這個步驟,纔會在報錯時顯示爲用戶無權,而不是字段k不存在,此舉是爲了避免向用戶暴露表結構。三、詞法分析階段是從information_schema裏面得到表的結構信息的。四、可以使用鏈接池的方式,將短鏈接變爲長鏈接五、mysql_reset_connection 是MySQL爲各個語言提供的api,非sql語句。六、wait_timeout 是非交互式鏈接的空閒超時,interactive_timeout 是交互式鏈接的空閒超時。執行時間不計入空閒時間。經過客戶端鏈接的爲交互式,經過程序鏈接的是非交互式

相關文章
相關標籤/搜索