1. 程式人生 > >mysql中SQL執行過程詳解

mysql中SQL執行過程詳解

 mysql執行一個查詢的過程,到底做了些什麼:

 

  • 客戶端傳送一條查詢給伺服器;
  • 伺服器先檢查查詢快取,如果命中了快取,則立刻返回儲存在快取中的結果。否則進入下一階段。
  • 伺服器段進行SQL解析、預處理,在優化器生成對應的執行計劃;
  • mysql根據優化器生成的執行計劃,呼叫儲存引擎的API來執行查詢。
  • 將結果返回給客戶端。

    實際上mysql執行的每一步都比較複雜,具體的過程如下

 第一步mysql客戶端和伺服器通訊

 

    1. 通訊

    mysql客戶端和伺服器之間的通訊協議是“半雙工”的,這意味著,在任何一個時刻,要麼由伺服器向客戶端傳送資料,要麼由客戶端向伺服器傳送資料,這兩個動作不能同時發生。這種協議讓mysql通訊簡單快速,但也限制了mysql。一個明顯的限制是,這意味著沒辦法進行流量限制。一旦一端開始發生訊息,另一端要接收完整個訊息才能響應他。

    客戶端用一個單獨的資料包將查詢傳給伺服器。一旦客戶端傳送了請求,他能做的事情就只是等待結果了。

    相反的,一般伺服器響應給使用者的資料通常很多,由多個數據包組成。當伺服器開始響應客戶端請求時,客戶端必須完整的接受整個返回結果,而不是簡單的只收取前面幾條結果,然後讓伺服器停止傳送資料。

    多數連線mysql的庫函式都可以獲得全部結果並快取到記憶體裡,還可以逐行獲取所需要的資料。預設一般是獲得全部結果並快取到記憶體中。mysql通常需要等所有的資料都已經發送給客戶端才能釋放這條查詢所佔用的資源,所以接受全部結果並快取通常可以減少伺服器的壓力,讓查詢能夠早點結束、早點釋放對應的資源。

 2.應用程式把查詢SQL語句傳送給伺服器端執行

在資料庫層執行SQL語句時,應用程式會連線到相應的資料庫伺服器,把SQL語句傳送給伺服器處理。

 

第二步:查詢快取

 

在解析一個查詢語句之前,如果查詢快取(MySQL預設開啟,可以使用have_query_cache檢視)是開啟的,在接收到查詢請求後,mysql並不會直接去資料庫查詢,而是優先檢查這個查詢是否命中查詢快取中的資料(某條給定的查詢語句在第一次執行時,伺服器會快取這條查詢語句和他返回的結果)。

而其中是否命中快取是將此查詢語句和快取中的查詢語句進行比對,如果完全相同,那就認為它們是相同的,就認為命中快取了(是通過一個對大小寫敏感的雜湊查詢實現的)。

如果當前的查詢恰好命中了查詢快取,那麼在返回查詢結果之前mysql會檢查一次使用者許可權。這仍然是無須解析查詢SQL語句的,因為在查詢快取中已經存放了當前 查詢需要訪問的表資訊。如果許可權沒有問題,mysql會跳過所有其他階段,直接從快取中拿到結果並返回給客戶端。這種情況下,查詢不會被解析,不用生成執行計劃,不會被執行。

如果當前的查詢沒有命中查詢快取,這種情況下查詢就會進入下一階段的處理。

 

第三步:查詢優化處理,生成執行計劃

 

接下來伺服器會將一個SQL轉換成一個執行計劃,而這個階段包括:解析SQL預處理優化SQL執行計劃,其中任何一個階段出錯都會導致查詢進行不下去。然後mysql在依照這個執行計劃和儲存引擎進行互動。

 

  • 解析SQL:Mysql通過將SQL語句進行解析(語法解析器),並生成一棵對應的解析樹。MySQL解析器將使用MySQL語法規則驗證和解析查詢,如將驗證是否使用錯誤的關鍵字,或者關鍵字的順序是否正確。
  • 預處理:前處理器根據一些Mysql規則進一步檢查解析樹是否合法,如資料表和資料列是否存在,解析列名和別名,是否有歧義。接下來前處理器會驗證使用者許可權。檢視使用者是否有相應的操作許可權。。
  • 優化SQL:當語法樹被認為是合法的了,優化器將SQL語句轉化成執行計劃,一條查詢可以有很多種執行方式,最後都返回相同的結果,最後找到其中最好的執行計劃(Mysql使用基於成本的優化器,它將嘗試預測一個查詢使用某種執行計劃的成本,選擇其中成本最小的一個)。

    

    優化器的作用就是找到這其中最好的執行計劃。

 

第四步:查詢執行引擎

 

 在解析和優化階段,mysql將生成查詢對應的執行計劃,mysql的查詢執行引擎則根據這個執行計劃來完成整個查詢。這裡執行計劃是一個數據結構,而不是和很多其他的關係型資料庫那樣對應的位元組碼。

 mysql簡單的根據執行計劃給出的指令逐步執行。在根據執行計劃逐步執行的過程中,有大量的操作需要通過呼叫儲存引擎實現的介面來完成,這些介面即為“handler API”介面。為了執行查詢,mysql只需要重複執行計劃中的各個操作,直到完成所有的資料查詢。查詢中的每一個表由一個handler的例項表示。(實際上,在優化階段Mysql就為每一個表建立了一個handelr例項,優化器可以根據這些例項的介面獲取表的相關資訊,如表的所有列名、索引統計資訊等)

 

第五步:將查詢結果返回客戶端

 

查詢執行的最後一個階段是將結果返回給客戶端。即使查詢不需要返回結果給客戶端,mysql仍然會返回這個查詢的一些資訊,如該查詢影響到的行數。

如果查詢可以被快取,那麼mysql在這個階段也會將結果放到查詢快取中。

mysql將結果集返回客戶端是一個增量、逐步返回的過程。這樣有兩個好處:伺服器端無須儲存太多的結果,也就不會因為返回太多結果而消耗太多的記憶體;這樣處理也讓msyql客戶端第一時間獲得返回的結果。

結果集中的每一行都會以一個滿足mysql客戶端/伺服器通訊協議的包傳送,再通過tcp協議進行傳輸,在tcp傳輸的過程中,可能對mysql的封包進行快取然後批量傳輸。

 

-------------------------------------------

解析:

導致Mysql優化器選擇錯誤的執行計劃的原因:

 

  • Mysql的最優可能和你想的最優不一樣。你可能希望執行時間最短,但Mysql根據其成本計算得出的最優計劃,可能執行時間並不是最短的。
  • 優化器有時候可能會無法估算所有的可能的執行計劃,導致有可能錯誤實際上最優的執行計劃。
  • 執行計劃中成本估算不等同於實際執行的成本。如有時候執行計劃需要讀取更多的頁面,但它成本卻更小。Mysql層面無法知道哪些頁面在記憶體中,哪些在磁碟上,所以實際執行過程中需要多少次物理I/O無法得知。
  • Mysql不會考慮不受其控制的操作的成本,如執行使用者自定義的函式的成本。
  • 統計資訊不準確:Mysql依賴儲存引擎提供的統計資訊(每個表有多少個頁面、資料行和索引的長度、索引的分佈等)來估計成本,有的儲存引擎提供的資訊偏差可能比較大。如InnoDB因為MVCC的架構,並不能維護一個數據表的行數的精確統計資訊。

 

查詢結果/語句不會被快取的情況

  • 如果表上有任何鎖,對這個表的任何查詢語句都是無法被快取的。
  • 查詢語句中有一些不確定的資料。如now()。
  • 有使用者自定義函式、使用者變數不會被快取。
  • 包含任何不確定函式的語句。
  • 當查詢的結果大於快取大小時,結果不會被快取

 

Mysql能夠處理的優化型別(可以做出的優化措施)

  • 重新定義關聯表的順序。資料表的關聯並不是總按照查詢中指定的順序進行。
  • 使用等價轉換規則。如移除一些恆成立或恆不成立的判斷。
  • 可能的表示式轉換為常熟表示式
  • 提前終止查詢。如使用limit。

執行計劃


Mysql並不會生成查詢位元組碼來執行查詢。Mysql生成查詢的一棵指令樹,然後通過儲存引擎執行完成這棵指令樹並返回結果。Mysql關聯執行的策略很簡單:Mysql對任何關聯都執行巢狀迴圈關聯操作,即Mysql先在一個表中迴圈取出單條資料,然後再巢狀到下一個表中尋找匹配的行,依次下去,直到找到所有表中匹配的行為止。然後根據各個表匹配的行,返回查詢中需要各個列。Mysql會嘗試在最後一個關聯表中找到所有匹配的行,如果最後一個關聯表無法找到更多的行以後,Mysql返回到上一層此關聯表,看能否找到更多的匹配記錄,依次類推迭代執行。所以Mysql的執行計劃總是一顆左側深度優先的樹。