04.07 【MySQL數據庫】你為什麼要這樣優化MySQL查詢?

【MySQL數據庫】你為什麼要這樣優化MySQL查詢?

MySQL數據庫是最常用的關係型數據庫之一,它執行一條查詢語句,通常要進行如下幾步:

  1. MySQL客戶端向MySQL服務器發送一條查詢語句。

  2. MySQL服務器先會檢查MySQL的查詢緩存,如果查詢語句命中了緩存,則MySQL服務器立即返回存儲在緩存中的結果。否則進入下一階段。

  3. 如果查詢語句沒有命中MySQL的查詢緩存,MySQL會先對查詢語句進行SQL解析、預處理,然後,再由優化器生成對應的執行計劃。

  4. MySQL服務器根據優化器生成的執行計劃,調用存儲引擎的API來執行查詢。

  5. 將結果返回給MySQL客戶端。

如果使用一幅圖來描述,則可以如下圖這樣。

【MySQL數據庫】你為什麼要這樣優化MySQL查詢?

下面針對其中關鍵的幾步進行一個詳細說明。

一、查詢緩存(query cache)

MySQL服務器在執行一個查詢時,會先檢查MySQL查詢緩存,而檢查MySQL查詢緩存時,會先檢查其查詢緩存是否打開的,如果是打開的,那麼會先檢查這個查詢語句是否命中了查詢緩存中的數據。如果命中了緩存中的數據,MySQL會檢查一次用戶權限,如果權限沒有問題,MySQL會跳過所有其他階段,直接從查詢緩存中拿到結果並返回給MySQL客戶端,否則直接進入下一個環節。

注意:MySQL檢查某個查詢是否命中緩存是通過一個對大小寫敏感的哈希查找實現的。查詢語句必須與緩存完全匹配才算命中。另外,如果查詢語句命中了查詢緩存,查詢不會被解析,也不會生成執行計劃和被執行。

控制MySQL查詢緩存,通常是通過設置MySQL的緩存配置,來實現的。如下列出了幾個控制MySQL查詢緩存的參數:

【MySQL數據庫】你為什麼要這樣優化MySQL查詢?

1、query_cache_limit

該參數表示MySQL服務器能夠緩存的最大結果。如果超出,則增加 qcache_not_cached的值,並刪除查詢結果

2、query_cache_min_res_unit:

該參數表示分配內存塊時的最小單位大小。

3、query_cache_size

該參數表示緩存使用的總內存空間大小,單位是字節,這個值必須是1024的整數倍,否則MySQL服務器實際分配可能跟這個數值不同。

4、query_cache_type

該參數表示是否打開緩存。OFF:關閉;ON:總是打開。

5、query_cache_wlock_invalidate

該參數表示如果某個數據表被鎖住,是否仍然從緩存中返回數據,默認是OFF,表示仍然可以返回。

二、解析器

如果查詢語句沒有命中查詢緩存,MySQL會對SQL語句進行解析。MySQL通過關鍵字將SQL語句進行解析,生成一棵對應的“解析樹”。MySQL解析器將使用MySQL語法規則驗證和解析查詢。例如,它將驗證是否使用了錯誤的關鍵字,或者使用關鍵字的順序是否正確,再或者它還會驗證引號是否能前後正確的匹配等。

三、預處理器

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

四、查詢優化器

待解析樹被預處理後,MySQL查詢優化器會將其轉化為執行計劃。通常一個查詢可以有很多種執行方式,但最後都返回相同的結果,查詢優化器的作用就是在這多種執行計劃中找到最好的一種。

MySQL使用基於成本的優化器,它將嘗試預測一個查詢使用某種執行計劃的成本,並選擇其中成本最小的一個。最初,成本的最小單位是隨機讀取一個4K數據頁的成本,後來成本計算公式變得更加複雜,並且引入了一些“因子”來估算某些操作的代價,如執行一次where條件比較的成本。可以通過查詢當前會話的last_query_cost的值來得知MySQL計算的當前查詢成本。

有很多種原因會導致MySQL優化器選擇錯誤的執行計劃,比如:

  • 統計信息不準確。

  • 執行計劃中的成本估算不等同於實際的執行計劃的成本。

  • MySQL的最優可能與你想的最優不一樣。

  • MySQL從不考慮其他併發的查詢,這可能會影響當前查詢的速度。

  • MySQL也不是任何時候都是基於成本的優化,有時候也會基於一些固定的規則。

  • MySQL不會考慮不受其控制的成本,例如執行存儲過程或者用戶自定義的函數的成本。

MySQL的查詢優化使用了很多優化策略來生成一個最優的執行的計劃,大致可以分為兩種,即:靜態優化和動態優化。

靜態優化可以直接對解析樹進行分析,並完成優化。例如,優化器可以通過一些簡單的代數變換將where條件轉換成另一種等價形式。靜態優化不依賴於特別的數值,例如,where條件中帶入的一些常數等。靜態優化在第一次完成後就一直有效,即使使用不同的參數重複查詢也不會變化,可以認為是一種“編譯時優化”。

相反,動態優化則和查詢的上下文有關。也可能和很多其他因素有關,例如,where條件中的取值、索引中條目對應的數據行數等,這些需要每次查詢的時候重新評估,可以認為是“運行時優化”。

下面是一些MySQL能夠處理的優化類型:

1、重新定義關聯表的順序

數據表的關聯並不總是按照在查詢中指定的順序進行,決定關聯的順序是優化器很重要的一部分功能。

2、將外連接轉化成內連接

並不是所有的outer join語句都必須以外連接的方式執行。例如,where條件、庫表結構都可能會讓外連接等價於一個內連接。MySQL能夠識別這點並重寫查詢,讓其可以調整關聯順序。

3、使用等價變換規則

MySQL可以使用一些等價變換來簡化並規範表達式。它可以合併和減少一些比較,還可以移除一些恆成立和一些恆不成立的判斷。例如,(5=5 and a>5)將被改寫為a>5。類似的,如果有(a5 and b=c and a=5。

4. 優化count()、min()、max()

索引和列是否為空通常可以幫助MySQL優化這類表達式。例如,要找到一列的最小值,只需要查詢對應B-tree索引最左端的記錄,MySQL可以直接獲取索引的第一行記錄。在優化器生成執行計劃的時候就可以利用這一點,在B-tree索引中,優化器會講這個表達式最為一個常數對待。類似的,如果要查找一個最大值,也只需要讀取B-tree索引的最後一個記錄。如果MySQL使用了這種類型的優化,那麼在explain中就可以看到“select tables optimized away”。從字面意思可以看出,它表示優化器已經從執行計劃中移除了該表,並以一個常數取而代之。

類似的,沒有任何where條件的count(*)查詢通常也可以使用存儲引擎提供的一些優化,例如,MyISAM維護了一個變量來存放數據表的行數。

5、預估並轉化為常數表達式

6、覆蓋索引掃描

當索引中的列包含所有查詢中需要使用的列的時候,MySQL就可以使用索引返回需要的數據,而無需查詢對應的數據行。

7、子查詢優化

MySQL在某些情況下可以將子查詢轉換成一種效率更高的形式,從而減少多個查詢多次對數據進行訪問。

8、提前終止查詢

在發現已經滿足查詢需求的時候,MySQL總是能夠立即終止查詢。一個典型的例子就是當使用了limit子句的時候。除此之外,MySQL還有幾種情況也會提前終止查詢,例如發現了一個不成立的條件,這時MySQL可以立即返回一個空結果。

【MySQL數據庫】你為什麼要這樣優化MySQL查詢?

上面的例子可以看出,查詢在優化階段就已經終止。

9、等值傳播

10、列表in()的比較

在很多數據庫系統中,in()完全等同於多個or條件的字句,因為這兩者是完全等價的。在MySQL中這點是不成立的,MySQL將in()列表中的數據先進行排序,然後通過二分查找的方式來確定列表中的值是否滿足條件,這是一個o(log n)複雜度的操作,等價轉換成or的查詢的複雜度為o(n),對於in()列表中有大量取值的時候,MySQL的處理速度會更快。

五、查詢執行引擎

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

相對於查詢優化階段,查詢執行階段不是那麼複雜,MySQL只是簡單的根據執行計劃給出的指令逐步執行。在根據執行計劃逐步執行的過程中,有大量的操作需要通過調用存儲引擎實現的接口來完成,這些接口就是我們稱為“handler API”的接口。實際上,MySQL在優化階段就為每個表創建了一個handler實例,優化器根據這些實例的接口可以獲取表的相關信息,包括表的所有列名、索引統計信息等。

六、返回結果給客戶

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

如果查詢可以被緩存,那麼MySQL在這個階段,會將結果存放到查詢緩存中。

MySQL將結果返回客戶端是一個增量、逐步返回的過程。例如,在關聯表操作時,一旦服務器處理完最後一個關聯表,開始生成第一條結果時,MySQL就可以開始向客戶端逐步返回結果集了。

這樣處理有兩個好處:

  1. 服務器無需存儲太多的結果,也就不會因為要返回太多的結果而消耗太多的內存。

  2. 這樣的處理也讓MySQL客戶端第一時間獲得返回的結果。

結果集中的每一行都會以一個滿足MySQL客戶端/服務器通信協議的封包發送,再通過TCP協議進行傳輸,在TCP傳輸過程中,可能對MySQL的封包進行緩存然後批量傳輸。


【MySQL數據庫】你為什麼要這樣優化MySQL查詢?



分享到:


相關文章: