判斷sql 是否合法 SQL 語句的內部處理邏輯

2021-10-14 19:58:43 字數 2839 閱讀 3551

mysql 的基本架構示意圖如下:

連線管理是 sql 語句執行過程中碰到的第一關,鏈結管理就像一扇大門一樣,控制著客戶端與 server 服務端的互動,連線管理主要工作是客戶端的身份認證和連線線程的管理。

每個客戶端與 server 建立連線時,服務端都會建立乙個執行緒來與客戶端進行互動,互動的第一項內容就是驗證客戶端的身份,認證憑據是基於客戶端發起連線請求時攜帶的主機資訊、使用者名稱、密碼。如果認證失敗,則結束連線任務,並且返回的access denied for user錯誤。

如果認證成功,連線管理還會做一件事情,到許可權表中查詢出該使用者的許可權,在這次連線下,後續的許可權判斷都是基於此時讀取的許可權為依據,也就是說連線成功後,即使管理員對這個使用者做了許可權修改,也不會影響這次連線的許可權驗證。

連線管理需要做的事情就比較簡單,主要是負責客戶端與服務端進行連線,當然在連線線程上,連線管理也做了優化,並不是每個客戶端執行完任務之後,就把該執行緒銷毀,連線管理會把這些執行緒快取起來,等待新的連線,這也就不會頻繁的建立和銷毀執行緒,從而節約了開銷。

完成連線管理之後,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 儲存引擎。

pb 判斷sql 是否合法 檢查SQL語句是否合法

昨天又有乙個新的需求 驗證文字框輸入的sql語法是否正確。首先需要知道 set parseonly 當 set parseonly 為 on 時,sql server 只分析語句。當 set parseonly為 off 時,sql server 編譯並執行語句。和 set parseonly 相關...

pb 判斷sql 是否合法 檢查SQL語句是否合法

昨天又有乙個新的需求 驗證文字框輸入的sql語法是否正確。首先需要知道 set parseonly 當 set parseonly 為 on 時,sql server 只分析語句。當 set parseonly為 off 時,sql server 編譯並執行語句。和 set parseonly 相關...

檢查SQL語句是否合法

昨天又有乙個新的需求 驗證文字框輸入的sql語法是否正確。首先需要知道 set parseonly 當 set parseonly 為 on 時,sql server 只分析語句。當 set parseonly 為 off 時,sql server 編譯並執行語句。和 set parseonly 相...