【MySQL學習筆記(十二)】之 查詢優化器基於規則的優化與子查詢優化
本文章由公號【開發小鴿】釋出!歡迎關注!!!
老規矩–妹妹鎮樓:
一. 查詢優化器基於規則的優化
(一) 概述
查詢優化器針對查詢語句根據相應的規則進行優化,轉換為某種可以高效執行的形式,這個過程稱為查詢重寫。
(二) 條件化簡
1. 移除不必要的括號
將表示式中多餘的括號去掉。
2. 常量傳遞
若某個列和某個常量已經進行了等值匹配,當使用AND操作符將這個表示式和其他涉及該列的表示式連線起來的時候,可以將該列轉換為常量。
如:
a = 5 AND b > a
a = 5 AND b > 5
3. 移除已知的條件
對於明顯為TRUE或者FALSE的表示式,優化器會將它們移除。
4. 表示式計算
在查詢執行之前,如果表示式中只包含常量的話,它的值會被先計算出來。
5. 常量表檢測
對於記錄數不大於1的表或者使用主鍵,唯一二級索引列等值匹配的表,由於這兩種表查詢花費時間很少,稱為常量表。查詢優化器在分析一個查詢語句時,首先執行常量表檢測,看看有沒有常量表,然後將查詢中涉及該表的條件全部替換為常數,再分析其他表的查詢成本。
(三) 外連線消除
為什麼要消除外連線呢?因為內連線能夠通過優化器評估表的不同連線順序的成本,選出成本最低的連線順序來執行查詢。
知道了原因後,來看看怎麼消除。凡是不符合WHERE子句條件的記錄都不會參與連線,只要我們在WHERE子句的搜尋條件中指定被驅動表的列不為NULL(這種條件稱為空值拒絕),那麼那些在外連線被驅動表中匹配不到ON子句條件的驅動表記錄就不會出現在最終的結果集中,此時的外連線與內連線是一樣的,也就達到了消除外連線的目的。
二. 子查詢優化
(一) 子查詢
1. 概述
子查詢就是在查詢語句中巢狀一個查詢,不論是在FROM子句,WHERE子句,ON子句等等中都可以巢狀子查詢,外面的查詢稱為外層查詢。放到FROM子句中的子查詢稱為派生表,因為相當於查詢一個表。
2. 按照返回的結果集分類
(1) 標量子查詢
只返回一個單一值的子查詢。
(2) 行子查詢
返回一條記錄的子查詢,該記錄需要包含多個列。
(3) 列子查詢
查詢出一個列的資料,需要包含多條記錄。
(4) 表子查詢
子查詢的結果既包含多條記錄,也包含多個列。
3. 按照與外層查詢的關係分類
(1) 不相關子查詢
子查詢單獨執行出結果,不依賴外層查詢的值。
(2) 相關子查詢
子查詢的執行需要依賴於外層查詢的值。
4. 子查詢在布林表示式中的使用
(1) 與比較操作符一起使用
(2) IN/NOT IN
運算元 [NOT] IN (子查詢)
是否(不)存在於查詢結果集組成的集合中。
(3) ANY/SOME
運算元 比較操作符 ANY/SOME(子查詢)
只要在子查詢的結果集中存在一個值,某個指定的運算元與該值通過比較操作符進行比較時結果為TRUE,則整個表示式為TRUE。
(4) ALL
運算元 比較操作符 ALL(子查詢)
只有某個指定的運算元與子查詢的結果集中所有的值通過比較操作符進行比較時結果為TRUE,整個表示式才會為TRUE。
(5) EXISTS
[NOT] EXISTS (子查詢)
只需要判斷子查詢的結果集中是否有記錄,而不在乎記錄具體是什麼。
5. 子查詢注意事項
(1) 子查詢必須用小括號
(2) SELECT子句中的子查詢必須是標量子查詢
(3) 不允許在一條語句中增刪改某個表的記錄時,同時對該表子查詢
(二) 子查詢在MySQL中如何執行的
1. 標量子查詢,行子查詢
(1) 不相關子查詢
單獨執行子查詢,然後將子查詢得到的結果當做外層查詢的引數,再執行外層查詢,即多個單表查詢。
(2) 相關子查詢
先從外層查詢中獲取一條記錄,然後從這條記錄中找出子查詢涉及的值執行子查詢,最後根據子查詢的查詢結果來檢測外層查詢中的條件是否成立,如果成立就把外層查詢的那條記錄加入到結果集中,否則丟棄。
2. IN子查詢優化
(1) 物化表
對於不相關的IN子查詢,如果子查詢的結果集太大可能會導致效能問題,如記憶體放不下,無法有效使用索引。因此不直接將結果集當做外層查詢的引數,而是將結果集寫入一個臨時表中,對寫入的記錄進行去重以節省空間。臨時表是基於記憶體的使用MEMORY儲存引擎的,還會為該表建立雜湊索引,如果結果集特別大,會將臨時錶轉換為基於磁碟的儲存引擎來儲存結果集,索引也會變為B+樹索引。這種建立臨時表的過程稱為物化,臨時表稱為物化表。
既然生成了新的表,就可以將該物化表與其他的表進行內連線操作了,也就可以通過計算成本來找出成本最低的執行方式了。
(2) 半連線
物化表有建立臨時表的成本,MySQL提出了半連線,直接將子查詢轉換為連線。將s1表和s2表進行半連線的意思就是對於s1表中的記錄來說,只關心在s2表中是否存在與之匹配的記錄,而不關心具體有多少條記錄與之匹配,最終的結果集中只保留s1表的記錄。
共有5中半連線的執行策略,且半連線只是MySQL內部的優化策略, 不是向用戶開放的介面。
3. [NOT] EXISTS子查詢
如果是不相關子查詢,先執行子查詢,得出結果是TURE還是FALSE後,重寫原先的查詢語句。如果是相關子查詢,只能按照之前標量子查詢中相關子查詢的方式查詢,捕獲EXISTS子查詢可以使用索引加快速度。
4. 派生表的優化
放在FROM子句中的子查詢就是派生表,MySQL提供了兩種執行策略。一種是將派生表物化,寫入到一個臨時表中,將這個物化表當做普通表一樣來參與查詢,MySQL中還有一種延遲物化的策略,在查詢時真正用到派生表時才會物化派生表。還有一種就是將派生表與外層查詢合併,寫成沒有派生表的形式,將派生表提取到FROM子句中,搜尋條件合併到WHERE子句中。
MySQL優先嚐試派生表和外層查詢的合併,如果不行,就物化派生表,執行查詢。