MySQL查詢過程和高階查詢
通過中篇的介紹,你會了解到:
MySQL查詢過程
高階查詢相關概念
explain命令詳細介紹
索引優化建議
MySQL查詢過程
想要更好的優化查詢,首先要了解其整體查詢過程,從客戶端傳送查詢請求,到接收到查詢結果,MySQL伺服器做了很多工作。
邏輯架構
MySQL邏輯架構整體分為三層,分別為客戶端層、核心服務層、儲存引擎層,共同協作完成。
最上層為客戶端層,比如:連線處理、授權認證、安全等功能等。
中間層是MySQL的核心服務,包括查詢解析、分析、優化、快取、內建函式(比如:時間、數學、加密等),另外,所有的跨儲存引擎的功能也在這一層實現:儲存過程、觸發器、檢視等。
最下層為儲存引擎,負責資料儲存和提取,中間的服務層通過API與儲存引擎通訊,這些API介面遮蔽了不同儲存引擎間的差異。
具體執行過程
重點看下MySQL是如何優化和執行查詢的,很多的查詢優化工作就是遵循一些原則讓MySQL的優化器能夠按照預想的方式執行而已。
先說下總體流程:
-
客戶端傳送一條查詢SQL給伺服器;
-
伺服器先檢查查詢快取,如果命中了快取,則立即返回儲存在快取中的結果;
-
伺服器端進行SQL解析、預處理,再由優化器生成對應的執行計劃;
-
查詢執行引擎根據優化器生成的執行計劃,呼叫儲存引擎的API來執行查詢;
-
將結果返回給客戶端;
1.客戶端/服務端通訊協議
MySQL客戶端和伺服器之間的通訊協議是「半雙工」:在任何一個時刻,要麼由伺服器向客戶端傳送資料,要麼由客戶端向伺服器傳送資料,不能同時發生,這也就意味著沒法進行流量控制。
客戶端用一個單獨的資料包將查詢請求傳送給伺服器,伺服器響應給使用者的資料通常會很多,由多個數據包組成,需要注意的是當伺服器響應客戶端請求時,客戶端必須完整的接收整個返回結果,而不能簡單的只取前面幾條結果,然後讓伺服器停止傳送。
2.查詢快取
如果查詢快取是開啟的,會檢查這個查詢語句是否命中查詢快取中的資料,如果命中,在檢查一次使用者許可權後直接返回快取中的結果。
查詢快取系統會跟蹤查詢中涉及的每個表,在任何的寫操作時,MySQL必須將對應表的所有快取都設定為失效,如果查詢快取非常大或者碎片很多,這個操作就可能帶來很大的系統消耗。
另外,任何的查詢語句在開始之前都必須經過檢查,即使這條SQL語句永遠不會命中快取,如果查詢結果可以被快取,那麼執行完成後,會將結果存入快取,也會帶來額外的系統消耗。
所以,開啟快取要慎重,只有當快取帶來的資源節約大於其本身消耗的資源時,才會給系統帶來效能提升,可以將query_cache_type設定為DEMAND,這時只有加入SQL_CACHE的查詢才會走快取,其他查詢則不會。
3.語法解析和預處理
通過關鍵字將SQL語句進行解析,生成一顆解析樹,預處理則會根據MySQL規則進一步檢查解析樹是否合法。
4.查詢優化
一條查詢可以有很多種執行方式,優化器的作用就是找到這其中最好的執行計劃,MySQL使用基於成本的優化器,它嘗試預測一個查詢使用某種執行計劃時的成本,並選擇其中成本最小的一個。
5.查詢執行引擎
儲存引擎介面提供了非常豐富的功能,但其底層僅有幾十個介面,這些介面像搭積木一樣完成了一次查詢的大部分操作。
6.返回結果給客戶端
結果集返回客戶端是一個增量且逐步返回的過程,這樣服務端就無須儲存太多結果而消耗過多記憶體,也可以讓客戶端第一時間獲得返回結果。
SELECT執行順序
下面來看看SQL查詢語句的執行順序,每一步都會生成一個虛擬臨時表,作為下一步的輸入。
標準的SQL語法如下:
SELECT DISTINCT
< select_list >
FROM
< left_table > < join_type >
JOIN < right_table > ON < join_condition >
WHERE
< where_condition >
GROUP BY
< group_by_list >
HAVING
< having_condition >
ORDER BY
< order_by_condition >
LIMIT < limit_number >
但執行順序是這樣的:
FROM <left_table> ON <join_condition> <join_type> JOIN <right_table> WHERE <where_condition> GROUP BY <group_by_list> HAVING <having_condition> SELECT DISTINCT <select_list> ORDER BY <order_by_condition> LIMIT <limit_number>
1.FROM
當涉及多個表的時候,左邊表的輸出會作為右邊表的輸入,之後會生成一個虛擬表VT1:
-
計算兩個相關聯表的笛卡爾積(CROSS JOIN) ,生成虛擬表VT1-J1;
-
基於虛擬表VT1-J1進行過濾,過濾出所有滿足ON謂詞條件的行,生成虛擬表VT1-J2;
-
如果使用了外連線(LEFT,RIGHT,FULL),主表(保留表)中的不符合ON條件的列也會被加入到VT1-J2中,生成虛擬表VT1-J3;
2.WHERE
對VT1過程中生成的臨時表進行過濾,滿足WHERE子句的列被插入到VT2表中:
-
與ON的區別:如果有外連線,ON針對過濾的是關聯表,主表會返回所有的列,如果沒有外連線,效果相同;
-
對主表的過濾應該放在WHERE;
-
於關聯表,先條件查詢後連線則用ON,先連線後條件查詢則用WHERE;
3.GROUP BY
這個子句會把VT2中生成的表按照GROUP BY中的列進行分組,生成VT3表。
其後處理過程的語句,如SELECT,HAVING,所用到的列必須包含在GROUP BY中,對於沒有出現的,得用聚合函式;
4.HAVING
對VT3表中的不同的組進行過濾,只用於分組後的資料,滿足HAVING條件的子句被加入到VT4表中。
5.SELECT
這個子句對SELECT子句中的元素進行處理,生成VT5表:
-
計算SELECT子句中的表示式,生成VT5-J1;
-
DISTINCT:尋找重複列,並刪掉,會建立一張記憶體臨時表VT5-J2,和虛擬表VT5-J1一樣,不同的是對DISTINCT的列增加唯一索引,以此來除重複資料;
6.ORDER BY
從VT5-J2中的表中,根據ORDER BY 子句的條件對結果進行排序,生成VT6表,這是唯一可使用SELECT中別名的地方。
7.LIMIT
從上一步得到的VT6虛擬表中選出從指定位置開始的指定行資料。
高階查詢相關概念
連線查詢
將多張表按照某個指定的條件進行資料拼接,SQL中將連線查詢分成四類: 內連線、外連線、自然連線、交叉連線,其中自然連線和交叉連線很少用到,就不過多介紹了。
1.內連線 inner join
從左表中取出每一條記錄,分別與右表中所有的記錄進行匹配,匹配必須左表和右表中都滿足條件,匹配的會保留結果,否則不保留。
2.外連線 left/right join
外連線分為兩種:
-
left join: 左外連線(左連線),以左表為主表;
-
right join: 右外連線(右連線),以右表為主表;
以某張表為主,取出裡面的所有記錄,不管能不能匹配上條件,主表最終都會保留,然後與另外一張表進行連線,如果不能匹配,其他表的欄位都置空NULL。
子查詢
是在某個查詢結果之上再進行查詢,也就是一條select語句內部包含了另外一條select語句。
按子查詢所在位置,可以劃分為:
-
From子查詢:子查詢跟在from之後;
-
Where子查詢: 子查詢出where條件中;
-
exists子查詢: 子查詢出現在exists裡面;
下面舉幾個例子:
查詢部門名稱字首為「小米」的所有員工:
SELECT name , sex , sal
FROM emp
WHERE no in (
SELECT no FROM dept
WHERE name LIKE '小米%'
);
檢視所有員工的薪水,並按薪水排序:
SELECT name , sal
FROM (
SELECT name , sal
FROM emp ORDER BY sal
);
聯合查詢
將多次查詢, 將結果進行拼接,欄位不會增加,每一條select語句獲取的欄位數必須嚴格一致。
語法如下:
Select 語句1
Union [union選項]
Select語句2...
Union選項:
-
All: 保留所有;
-
Distinct: 去重,預設選項;