1客戶端發送一條查詢給服務器;
2服務器會先檢查查詢緩存,若是命中了緩存,則當即返回存儲在緩存中的結果。不然進入下一階段;
3服務器端進行SQL解析、預處理、再由優化器生成對應的執行計劃;
4MySQL根據優化器生成的執行計劃,調用存儲引擎的API來執行查詢;
5將結果返回客戶端。數據庫
在解析一個查詢語句以前,若是查詢緩存是打開的,那麼MySQL會優先檢查這個查詢是否命中查詢緩存中的數據。這個檢查是經過一個對大小寫敏感的哈希查找實現的。查詢和緩存中的查詢即便只有一個字節不一樣,那也不會匹配緩存結果,這種狀況查詢會進入下一個階段的處理。緩存
若是當前的查詢剛好命中了查詢緩存,那麼在返回查詢結果以前MySQL會檢查一次用戶權限。這仍然是無須解析查詢SQL語句的,由於在查詢緩存中已經存放了當前查詢須要訪問的表信息。若是權限沒有問題,MySQL會跳過全部其餘階段,直接從緩存中拿到結果並返回給客戶端。這種狀況下,查詢不會被解析,不用生成執行計劃,不會被執行。服務器
緩存配置參數:數據結構
query_cache_limit: MySQL可以緩存的最大結果,若是超出,則增長 Qcache_not_cached的值,並刪除查詢結果併發
query_cache_min_res_unit: 分配內存塊時的最小單位大小函數
query_cache_size: 緩存使用的總內存空間大小,單位是字節,這個值必須是1024的整數倍,不然MySQL實際分配可能跟這個數值不一樣(感受這個應該跟文件系統的blcok大小有關)優化
query_cache_type: 是否打開緩存 OFF: 關閉 ON: 老是打開spa
query_cache_wlock_invalidate: 若是某個數據表被鎖住,是否仍然從緩存中返回數據,默認是OFF,表示仍然能夠返回3d
首先,MySQL經過關鍵字將SQL語句進行解析,並生成一棵對應的「解析樹」。MySQL解析器將使用MySQL語法規則驗證和解析查詢。例如,它將驗證是否使用錯誤的關鍵字,或者使用關鍵字的順序是否正確等,再或者它還會驗證引號是否能先後正確的匹配。blog
預處理器則根據一些MySQL規則進一步檢查解析樹是否合法,例如,這裏講檢查數據表和數據列是否存在,還會解析名字和別名,看看它們是否有歧義。
下一步預處理器會驗證權限,這一般很快,除非服務器上有很是多的權限設置。
如今語法樹被認爲合法的了,而且由優化器將其轉化爲執行計劃。一條查詢能夠由不少種執行方式,最後都返回相同的結果。優化器的做用就是找到這其中最好的執行計劃。
MySQL使用基於成本的優化器,它將嘗試預測一個查詢使用某種執行計劃的成本,並選擇其中成本最小的一個。最初,成本的最小單位是隨機讀取一個4K數據頁的成本,後來成本計算公式變得更加複雜,而且引入了一些「因子」來估算某些操做的代價,如當執行一次where條件比較的成本。能夠經過查詢當前會話的last_query_cost的值來得知MySQL計算的當前查詢的成本。
有不少種緣由會致使MySQL優化器選擇錯誤的執行計劃,好比:
1. 統計信息不許確。
2. 執行計劃中的成本估算不等同於實際的執行計劃的成本。
3. MySQL的最優可能與你想的最優不同。
4. MySQL從不考慮其餘併發的查詢,這可能會影響當前查詢的速度。
5. MySQL也不是任什麼時候候都是基於成本的優化,有時候也會基於一些固定的規則。
6. MySQL不會考慮不受其控制的成本,例如執行存儲過程或者用戶自定義的函數的成本。
MySQL的查詢優化使用了不少優化策略來生成一個最優的執行的計劃。優化策略能夠分爲兩種,靜態優化和動態優化。靜態優化能夠直接對解析樹進行分析,並完成優化。例如優化器能夠經過一些簡單的代數變換將where條件轉換成另外一種等價形式。靜態優化不依賴於特別的數值,如where條件中帶入的一些常數等。靜態優化在第一次完成後就一直有效,即便使用不一樣的參數重複查詢也不會變化,能夠認爲是一種「編譯時優化」。
相反,動態優化則和查詢的上下文有關。也可能和不少其餘因素有關,例如where條件中的取值、索引中條目對應的數據行數等,這些須要每次查詢的時候從新評估,能夠認爲是「運行時優化」。
下面是一些MySQL可以處理的優化類型:
1. 從新定義關聯表的順序
數據表的關聯並不老是按照在查詢中指定的順序進行,決定關聯的順序是優化器很重要的一部分功能。
2. 將外鏈接轉化成內鏈接
並非全部的outer join語句都必須之外鏈接的方式執行。諸多因素,例如where條件、庫表結構均可能會讓外鏈接等價於一個內鏈接。MySQL可以識別這點並重寫查詢,讓其能夠調整關聯順序。
3. 使用等價變換規則
MySQL可使用一些等價變換來簡化並規範表達式。它能夠合併和減小一些比較,還能夠移除一些恆成立和一些恆不成立的判斷。例如:(5=5 and a>5)將被改寫爲a>5。相似的,若是有(a<b and b=c)and a=5,則會被改寫爲 b>5 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能夠當即返回一個空結果。
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就能夠開始向客戶端逐步返回結果集了。
這樣處理有兩個好處:服務器無需存儲太多的結果,也就不會由於要返回太多的結果而消耗太多的內存。另外,這樣的處理也讓MySQL客戶端第一時間得到返回的結果。
結果集中的每一行都會以一個知足MySQL客戶端/服務器通訊協議的封包發送,再經過TCP協議進行傳輸,在TCP傳輸過程當中,可能對MySQL的封包進行緩存而後批量傳輸。