select * from user 這條 SQL 語句,背後藏着哪些不可告人的祕密?

做爲一名 Java開發人員,寫 SQL 語句是常有的事,可是你知道 SQL 語句背後的處理邏輯嗎?好比下面這條 SQL 語句:java

select * from user where id=1
複製代碼

執行完這條語句後,咱們就會獲得 id 爲 1 的用戶信息。那麼對於這一條 SQL 語句,MySQL服務器作了哪些處理呢?這篇文章咱們就一塊兒打卡 MySQL 數據庫中對 SQL 語句的處理邏輯。mysql

瞭解 MySQL 數據庫的 SQL 語句內部處理邏輯有什麼好處?當咱們碰到 MySQL 的一些異常或者問題時,就可以直戳本質,更爲快速地定位並解決問題算法

想要更好的瞭解 SQL 語句的內部處理邏輯,咱們能夠先看 MySQL 的基本架構圖,這樣咱們能夠站在更高的角度去俯瞰 MySQL 數據庫,MySQL 的基本架構示意圖以下:sql

MySQL 的基本架構示意圖

從圖中,咱們能夠清晰的看出 MySQL 的架構和各個模塊以及 SQL 語句的執行過程,MySQL 數據庫總體能夠分爲 Server 層和存儲引擎層兩部分,其中 Server 層是共有的,而存儲引擎層則是能夠以插件的形式進行擴展。一條 SQL 語句大概會經歷連接管理、解析與優化、最後到存儲引擎,這三個模塊。接下來咱們就來聊一聊這三個模塊。數據庫

鏈接管理

鏈接管理是 SQL 語句執行過程當中碰到的第一關,連接管理就像一扇大門同樣,控制着客戶端與 Server 服務端的交互,鏈接管理主要工做是客戶端的身份認證和鏈接線程的管理。緩存

每一個客戶端與 Server 創建鏈接時,服務端都會建立一個線程來與客戶端進行交互,交互的第一項內容就是驗證客戶端的身份,認證憑據是基於客戶端發起鏈接請求時攜帶的主機信息、用戶名、密碼。若是認證失敗,則結束鏈接任務,而且返回的 Access denied for user 錯誤。bash

若是認證成功,鏈接管理還會作一件事情,到權限表中查詢出該用戶的權限,在此次鏈接下,後續的權限判斷都是基於此時讀取的權限爲依據,也就是說鏈接成功後,即便管理員對這個用戶作了權限修改,也不會影響此次鏈接的權限驗證。服務器

鏈接管理須要作的事情就比較簡單,主要是負責客戶端與服務端進行鏈接,固然在鏈接線程上,鏈接管理也作了優化,並非每一個客戶端執行完任務以後,就把該線程銷燬,鏈接管理會把這些線程緩存起來,等待新的鏈接,這也就不會頻繁的建立和銷燬線程,從而節約了開銷。微信

解析與優化

完成鏈接管理以後,SQL 語句執行的第二步就是解析和優化,這一步就很是的複雜,SQL 語句查詢的全部操做都在這裏了。咱們能夠將這一步細分爲 4 小步。架構

查詢緩存

在 MySQL 服務端也有緩存,這是一個很是雞肋的功能,爲何呢?看完了你就知道了。

MySQL 服務器拿到查詢請求後,會先到查詢緩存看看,以前是否是執行過這條語句。以前執行過的語句及其結果可能會以 key-value 對的形式,被直接緩存在內存中。key 是查詢的語句,value 是查詢的結果。若是你的查詢可以直接在這個緩存中找到 key,那麼這個 value 就會被直接返回給客戶端。若是語句不在查詢緩存中,就會繼續後面的執行階段。執行完成後,執行結果會被存入查詢緩存中。

看上去沒毛病,這樣作會大大提高 MySQL 的性能,然而,你想多了,MySQL 的查詢緩存命中率很是的低,主要緣由是若是兩個查詢請求在任何字符上的不一樣(例如:空格、註釋、大小寫),都會致使緩存不會命中。

還有就是緩存有可能獲取到錯誤的數據,以某些系統函數舉例,可能一樣的函數的兩次調用會產生不同的結果,好比函數NOW,每次調用都會產生最新的當前時間,若是在一個查詢請求中調用了這個函數,那即便查詢請求的文本信息都同樣,那不一樣時間的兩次查詢也應該獲得不一樣的結果,若是在第一次查詢時就緩存了,那第二次查詢的時候直接使用第一次查詢的結果就是錯誤的!

除了這些以外,MySQL 緩存的失效也很是的頻繁,MySQL的緩存系統會監測涉及到的每張表,只要該表的結構或者數據被修改,如對該表使用了 INSERT、 UPDATE、DELETE、TRUNCATE TABLE、ALTER TABLE、DROP TABLE 或 DROP DATABASE 語句,那使用該表的全部高速緩存查詢都將變爲無效並從高速緩存中刪除!

看到這裏你知道查詢緩存很雞肋了吧,緩存對 MySQL 數據庫來講弊大於利,因此在 MySQL 8.0 版本直接將查詢緩存的整塊功能刪掉了

語法解析和預處理

若是查詢緩存沒有命中,接下來就須要進入正式的查詢階段了。由於客戶端程序發送過來的請求只是一段文本而已,因此 MySQL 服務器程序首先要對這段文本作語法解析。

首先經過關鍵字將 SQL 語句進行解析,而且生成一個「解析樹」。MySQL 解析器將使用 MySQL 語法規則驗證和解析查詢,例如,關鍵字是否使用正確、關鍵字的順序是否正確或者引號是否先後匹配等。

預處理是根據一些 MySQL 規則進一步檢查解析樹是否合法,例如數據表和數據列是否存在,還會解析名字和別名,看看他們是否有歧義等。

查詢優化

語法解析和預處理以後,你的需求就明白了,須要查詢哪張表,查詢的數據列是哪些、條件是什麼等等。可是使用怎麼樣的方式是最優查詢方式呢?查詢優化就是來幹這個事的,MySQL 的優化程序會對咱們的語句作一些優化,如外鏈接轉換爲內鏈接、表達式簡化、子查詢轉爲鏈接等等。優化的結果就是生成一個執行計劃,這個執行計劃代表了應該使用哪些索引進行查詢,表之間的鏈接順序是啥樣的。

執行器

執行器會執行查詢優化後的執行計劃,經過與存儲引擎交互,完成數據的查詢操做,返回最終的數據結果。

開始執行的時候,要先判斷一下你對這個表 T 有沒有執行查詢的權限,若是沒有,就會返回沒有權限的錯誤,以下所示 (在工程實現上,若是命中查詢緩存,會在查詢緩存返回結果的時候,作權限驗證。查詢也會在優化器以前調用 precheck 驗證權限)。

mysql> select * from user where ID=1;

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

若是有權限,就打開表繼續執行。打開表的時候,執行器就會根據表的引擎定義,去使用這個引擎提供的接口。

好比咱們這個例子中的表 user 中,假設 ID 字段沒有索引,那麼執行器的執行流程是這樣的:

一、調用 InnoDB 引擎接口取這個表的第一行,判斷 ID 值是否是 10,若是不是則跳過,若是是則將這行存在結果集中;

二、調用引擎接口取「下一行」,重複相同的判斷邏輯,直到取到這個表的最後一行。

三、執行器將上述遍歷過程當中全部知足條件的行組成的記錄集做爲結果集返回給客戶端。

到這裏,執行 SQL 語句就執行完了,其實這內部仍是很是複雜的。

存儲引擎

到上面爲止,SQL 語句就執行完了,可是與真實數據打交道的是存儲引擎,存儲引擎是 MySQL服務器對數據的存儲和提取操做的封裝模塊。咱們知道表是由一行一行的記錄組成的,但這只是一個邏輯上的概念,物理上如何表示記錄,怎麼從表中讀取數據,怎麼把數據寫入具體的物理存儲器上,這都是存儲引擎負責的事情。

爲了實現不一樣的功能,MySQL提供了各式各樣的存儲引擎,不一樣存儲引擎管理的表具體的存儲結構可能不一樣,採用的存取算法也可能不一樣。好比,MySQL5.7 以後默認的 InnoDB 存儲引擎。

能夠看出一條 SQL 語句的執行仍是很是複雜的,涉及到了不少的模塊,文章到這裏就結束了,感謝您的閱讀,但願這篇文章對你的學習和工做有所幫助,若是您以爲文章有用,歡迎點贊+轉發。

最後

目前互聯網上不少大佬都有 MySQL 內部架構相關文章,若有雷同,請多多包涵了。原創不易,碼字不易,還但願你們多多支持。若文中有所錯誤之處,還望提出,謝謝。

歡迎掃碼關注微信公衆號:「平頭哥的技術博文」,和平頭哥一塊兒學習,一塊兒進步。

平頭哥的技術博文
相關文章
相關標籤/搜索