MySQL之查詢效能優化二

2022-03-08 03:58:04 字數 4716 閱讀 2215

本文將會深入的扒一扒msyql查詢的流程,

也會講到一些mysql術語,如:快取,語法解析器,預處理,執行計畫,查詢優化器,

另外還會講一講mysql複雜查詢的基礎:"關聯"。

查詢快取:

在解析乙個查詢語句之前,如果查詢快取是開啟的,那麼mysql會優先檢查這個查詢是否命中查詢快取中的資料

查詢優化處理:

查詢的生命週期的下一步是將乙個sql轉換成乙個執行計畫,mysql再依照這個執行計畫和儲存引擎進行互動。

其中包含多個子階段:解析sql,預處理,優化sql執行計畫

這個過程中任何錯誤都將終止查詢。

語法解析器和預處理:

首先,mysql通過關鍵字將sql語句進行解析,並生產一棵對應的「解析樹」。

mysql解析器將使用mysql語法規則驗證和解析查詢。

預處理器則根據一些mysql規則進一步檢驗解析樹是否合法。

查詢優化器:

在這一步的時候,語法樹已經被認為合法了,並且由優化器將其轉換為執行計畫。

一條查詢可以有很多中執行方式,最後都返回相同的結果。

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

mysql使用基於成本的優化器,它將嘗試**乙個查詢使用某種執行計畫時的成本,並選擇其中成本最小的乙個。

可以通過查詢當前對話的last_query_cost的值來得知mysql計算的當前成本。

它是根據一系列的統計資訊計算得來的:每個表或者索引的頁面個數,索引的數(索引中不通值的數量)

索引和資料行的長度,索引分布情況。優化器在評估成本的時候並不考慮任何層面的快取,它假設讀取任何資料都需要一次磁碟io

有很多原因導致mysql優化器選擇錯誤的執行計畫。

1 統計資訊不準確

2 執行計畫中的成本估算不等同是你執行的成本。

3 mysql的最優可能和你想的最優不一樣。

4 mysql從不考慮其他併發執行的查詢,這可能會影響當前查詢的速度。

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

6 mysql不會考慮不受氣控制而操作的成本。

7 優化有時候無法估算所有可能的執行計畫,可能它錯過實際上最優的計畫。

mysql的查詢優化器優化策略主要分為兩種:

靜態優化, 可以直接對解析樹進行分析,並完成優化。又稱"編譯時優化"

動態優化, 動態優化則和上下文相關,也可能和很多因素有關,例如where條件中的取值,索引中條目對應的資料行數等。又稱"執行時優化"

mysql能夠優化的型別:

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

2. 將外連線轉換為內連線

3. 使用等價替換原則。 mysql可以使用一些等價變換來簡化並規範表示式。它可以合併和減少一些比較,還可以移除一些恆成立和一些恆不成立的判斷。

4. 優化count(),min(),max() 例如要找到某一列的最小值,只需要查詢對應b-tree索引最左端的紀錄,mysql可以直接獲取索引的第一行紀錄。

如果mysql使用了這種優化,將會在explain中看到 extra:"select tables optimized away"

5 .預估並轉化常數表示式

6. 覆蓋索引掃瞄

7. 子查詢優化

8. 提前終止查詢

9. 等值傳播

10.列表in()的比較

資料和索引的統計資訊:

統計資訊由儲存引擎實現,不同的儲存引擎可能會儲存不同的統計資訊。

因為伺服器層沒有任何統計資訊,所以mysql查詢優化器在生成查詢的執行計畫是,

需要向儲存引擎獲取相應的統計資訊。儲存引擎則提供給優化器對應的統計資訊,包括:

每個表或者索引又多少個頁面,每個表的每個索引的基數是多少,資料行和索引長度,

索引的分布資訊等。優化器根據這些資訊來選擇乙個最優的執行計畫。

mysql的關聯查詢策略:

mysql中的」關聯「術語並不是我們通常意義理解的關聯,它的意義更為廣泛。

總之,mysql認為不是僅僅是在兩張表查詢匹配行的查詢才是「關聯」,

而是所有的查詢都是「關聯」。

總之,理解mysql執行」關聯「的流程是非常重要的。

我們先來看乙個union查詢的例子。對於union查詢,mysql先將一系列的單個查詢結果

放在乙個臨時表內,然後再讀出來返回。在mysql的概念中,每個查詢都是一次關聯,

所以讀取臨時表也是一次關聯。

當前mysql關聯執行的策略很簡單:mysql對任何關聯都執行巢狀迴圈關聯操作,即mysql先在

乙個表中迴圈取出單條資料,然後再巢狀迴圈到下乙個表中尋找匹配的行,以此類推,直到

找到所有表中匹配的行為止。然後根據各個表匹配的行,返回查詢中需要的各個列。

按照這樣的方式查詢第乙個表記錄,再巢狀查詢下乙個關聯表,然後回溯到上乙個表,

在mysql中是通過巢狀迴圈實現---正如其名「巢狀迴圈關聯」。請看下面的例子中的簡單查詢:

mysql   -> select tbl1.col1, tbl2.col2

-> from tbl1 inner join tbl2 using(col3)

-> where tbl1.col1 in(5,6);

假設mysql按照查詢中表的順序進行關聯操作,我們則可以用下面的偽**表示mysql

如何完成這個查詢:

outer_iter = iterator over tbl1 where col1 in(5,6)

outer_row = outer_iter.next

while outer_row

inner_iter = iterator over tbl2 where col3 = outer_row.col3

inner_row = inner_iter.next

while inner_row

output [ outer_row.col1, inner_row.col2 ]

inner_row = inner_iter.next

endouter_row = outer_iter.next

end

上面的執行計畫對於單錶查詢和多表關聯查詢都適用,如果是乙個單錶查詢,那麼只需要

完成上面外層的基礎操作。對於外連線上面的執行過程仍然適用。如:

mysql  -> select tbl1.col1, tbl2.col2

-> from tbl1 left outer join tbl2 using(col3)

-> where tbl1.col1 in(5,6);

偽**如下:

outer_iter = iterator over tbl1 where col1 in(5,6)

outer_row = outer_iter.next

while outer_row

inner_iter = iterator over tbl2 where col3 = outer_row.col3

inner_row = inner_iter.next

if inner_row

while inner_row

output [ outer_row.col1, inner_row.col2 ]

inner_row = inner_iter.next

endelse

output [ outer_row.col1, null ]

endouter_row = outer_iter.next

end

另一種視覺化查詢執行計畫的方法是根據優化器執行的路徑繪製出對應的泳道圖。如圖:

執行計畫:

mysql會生成查詢的一顆指令樹,然後通過儲存引擎執行完成這顆指令樹並返回結果。

最終的執行計畫包含重構查詢的全部資訊。

如果對某個查詢 執行explain extended後,再執行show warnings就可以看到重構後的查詢。例:

explain extended

select * fromactivity;

show warnings;

任何多表查詢都可以使用一棵樹表示,mysql執行查詢的方式,並不是一顆平衡樹,

正如剛剛介紹的,mysql總是從乙個表開始一直巢狀迴圈,回溯完成所有表關聯。

所以,mysql的執行計畫總是如圖所示,是一顆左側深度優先的樹。

從本質上來講,mysql對所有型別的都是以同樣的方式執行。例如,mysql在from子句遇到子查詢時,先執行子查詢的並將其結果集放到乙個臨時表,

然後將這個表當做普通表對待。mysql在執行union查詢時也適用類似的臨時表,在遇到右外連線的時候會改寫為等價的左外鏈結。簡而言之,

當前版本的mysql會將所有的查詢型別都轉換成類似的執行計畫。 

《高效能MySQL》之MySQL查詢效能優化

響應時間過長。如果把查詢看做是乙個任務,那麼它由一系列子任務組成,每個子任務都會消耗一定的時間。如果要優化查詢,實際上優化其子任務,要麼消除其中一些子任務,要麼減少子任務的執行次數,要麼讓子任務執行得更快。查詢的生命週期 客戶端 伺服器 伺服器上解析 生成執行計畫 執行 返回結果給客戶端。其中 執行...

mysql查詢效能優化 MySQL 查詢效能優化

在日常開發中,程式設計師寫的最多的除了bug之外,應該算是sql語句了。sql的質量影響了程式的響應速度,只有利用mysql的特性,才能讓mysql更有效的執行查詢sql,充分發揮mysql的優勢,並避開它的弱點。為什麼查詢速度會慢?在編寫sql之前,需要清楚一點 真正重要的是響應時間。如果我們把查...

mysql查詢效能優化之二

1 union的限制 有時mysql無法將限制條件從外層下推到內層,這使得原本能夠限制部分返回結果的條件無法應用到內層 查詢的優化上。如果希望union的各個子句能夠根據limit只取部分結果集,或者希望能夠先排好序在 合併結果集的話,就需要在union的各個子句中分別使用這些子句。例如 想將兩個子...