MySQL學習 - 查詢的執行過程

xiaohan.liang?發表於2019-05-04
想要獲得更好的表現,你需要:  合理的表結構 + 出色的索引 + [不錯的查詢語句]


1. 慢查詢是怎麼回事


1.1 我怎麼才能知道我的查詢很慢

  • 開啟慢查詢日誌,比如你認為查詢超過1秒就是慢,MySQL會記錄下超過1秒的查詢記錄
  • 看看是不是因為MySQL任務太多導致你查詢很慢
  • 使用EXPLAIN , 仔細研究每一條語句,看看是不是執行了不該執行的內容


1.2 你是不是向資料庫請求了多餘的資料

很多時候你其實在向資料庫請求了超多資源,可能你並沒有意識到,這些多餘的資料會被拋棄,並給MySQL伺服器端增加額外的壓力。 一些場景:

  • 你取了所有資料,但是你的程式卻只用到前面10行:   
    • 如果你真的只需要10行,學會使用 LIMIT 10
  • 取了全部的列,但是實際用不到。這可能會使你喪失使用覆蓋索引的機會
    • 什麼是覆蓋索引?   假設你的索引是A+B,對應B-Tree內節點,有A+B的情況下可以查詢到完整資料儲存位置。 但是如果你 select A,B from users, 你直接讀B-Tree就完事了,你甚至都不用去找完整資料,更快。但是你還是選擇了 select * ,我們就需要回去找完整資料,去找你根本用不到的剩餘列
    • 除了覆蓋索引,你還可能給伺服器帶來許多不必要的IO壓力
  • 重複查詢的資料
    • 比如查詢頭像這種請求,完全可以通過快取,不一定每次都要重新請求


1.3 MySQL有沒有掃描多餘的行

-- 使用索引
EXPLAIN SELECT * FROM users WHERE id = 1\G
********************** 1.row **********************
             type: ref    
             key : id     
             rows: 10     

-- 刪除索引
EXPLAIN SELECT * FROM users WHERE id = 1\G
********************** 1.row ***********************
             type:  ALL        
             rows:  5073       
             extra: Using Where
複製程式碼

我們可以通過"EXPLAIN" 命令看看這條命令是怎麼執行的,有沒有索引掃描內容真的差別太大

  • 如果你有索引,並且通過索引檢索,我們使用 key=id 的 ref 方式,這種情況下,MySQL大概需要掃描10行,能得到你想要的資料
  • 但是如果你沒有索引了,我們現在只能通過先全表掃描 + USING WHERE 的方式來篩選了,這種情況下預計得掃描5073行才能得到你想要的資料
    • 真的掃描了太多本不需要的東西

在表面上,我們都直接在 SQL 語句加上WHERE就完事兒,並不過多的去關心效能問題,但是即使大家都是WHERE, 在"索引" 的輔助下也會存在很大的優劣之分

由好到差:

  • WHERE 篩選項 即 索引, 這個在儲存引擎層就能完成  ->  圖一所示案例
  • 索引能覆蓋掃描專案(使用覆蓋索引), 標誌是EXPLAIN顯示Extra=Using Index 操作手法為MySQL伺服器拿著索引前往B-Tree讀資料就結束,不用資料庫讀數
  • 沒有使用索引,直接使用WHERE,資料庫引擎需要先從表中讀出資料,返回給MySQL伺服器,然後MySQL用WHERE過濾,這樣一來資料庫引擎一定掃描了很多資料 -> 圖二所示

總結一下,無論是方法1.儲存引擎能直接訪問需要的行,還是2.直接前往B-Tree讀數,都好過全表掃描,返回所有資料然後由MySQL做過濾。 為了達到這樣的效果,儘可能把要用到的WHERE篩選項放到索引中去



2. 查詢的過程是怎樣的

âMYSQL æ¥è¯¢è¿ç¨âçå¾çæç´¢ç»æ

為了更好的做出優化,以及後面會提到的"快取命中",我們必須也要先知道查詢過程是怎樣的。 關於詳細的步驟,我們會在下面的環節描述一下各個部件是怎麼工作的

  1. 客戶端傳送一條請求給伺服器
  2. 伺服器先檢查快取,如果命中快取則直接返回結果,否則進入下一階段
  3. MySQL伺服器進行SQL解析,預處理,再由優化器生成執行計劃
  4. MySQL伺服器根據執行計劃呼叫儲存引擎API介面
  5. 將結果快取 並 返還給客戶端


2.1 客戶端 & MySQL 伺服器之間的互動

MySQL客戶端與MySQL伺服器之間的互動是半雙工的,也就是說同一時刻內只有其中一方向另一方傳送請求,這個請求可以是客戶端向伺服器傳送SQL語句,也可以是伺服器向客戶端返回所請求的資料,一旦客戶端傳送了請求以後,它所能做的就只有等待伺服器返回所請求的資料

  • 等待伺服器查詢並返回的結果比較漫長,所以一個比較好的辦法是把查詢返回的資料進行快取,下次需要用的時候直接從快取讀取即可,可以減輕伺服器的壓力
  • 當我們使用很多個連結傳送請求的時候,表面上看我們是從MySQL伺服器獲取資料,其實都是快取獲取資料,這樣可以很大程度提升效率


2.2 如何使用快取

在解析一個SQL語句之前伺服器會先看看是否有命中快取中的資料,也就是看看是否已經有快取上了。檢查的標準是通過對查詢語句的雜湊實現的,如果雜湊出的結果是一樣的就算命中,並且這個雜湊是對大小寫敏感的,也就是說哪怕是大小寫不一致都不能算命中


2.3 SQL解析 & 預處理

MySQL伺服器通過關鍵字將客戶端發來的SQL語句進行解析,解析器通過MYSQL語法對這條語句進行驗證,例如它將驗證是否使用了錯誤的關鍵字,關鍵字順序是否正確等。

前處理器則會去檢查它所請求的資料表以及列是否存在,並驗證許可權。


2.4 查詢優化器的優化原則

走到了這一步說明你的語句沒有問題,能執行,問題就是怎麼執行。 所以查詢優化器,會先找出很多個可能的做法,並嘗試找出最優解 優化原則? 找出成本最低的

mysql > SELECT SQL_NO_CACHE COUNT(*) FROM users
+----------+
| count(*) |
+----------+
|     5462 |
+----------+

mysql > SHOW STATUS LIKE 'Last_query_cost'
+-----------------+-------------+
| Variable_name   | Value       |
+-----------------+-------------+
| Last_query_cost | 1040.59     |
+-----------------+-------------+複製程式碼
  • 你可以使用以上的方式檢視一下,上一條執行語句的 ”執行成本“ 是多少
  • "執行成本" 說明MySQL伺服器認為需要載入1040個資料頁,並在其中做隨機查詢才可以
    • 這個 "執行成本" 是這麼考量出來的
      • 索引節點,涉及多少個頁面
      • 索引以及資料行的長度,索引的分佈情況
      • 但是優化器並不考慮有"快取" 這種東西,它假設每取一次數 == 一次磁碟IO
    • "執行成本"  一定是準確的嗎?
      • 一定不是,考量很多時候就無法考慮到全部情況,並且他也不知道什麼在記憶體裡什麼在磁碟上,在記憶體裡的都不用做磁碟IO
      • 但是大部分時候計算出來的執行成本會比人思考的更準確



2.5 MySQL優化器 - 重新定義關聯順序

2.5.1 先簡單介紹一下聯表的執行過程 (如果你知道就可以跳過了)

âmysql join æ³³éå¾âçå¾çæç´¢ç»æ

-- 上圖反應的就是下面個SQL語句的聯表過程
SELECT tbl1.col1 , tbl2.col2 
FROM tbl1 JOIN tbl2 USING(col3)
WHERE tbl1.col1 IN(5,6)複製程式碼

表之間的關聯遵循一種"巢狀" 的規則,用最簡單的話說就是先取表[tbl1]的第一行,去表[tbl2]中做匹配,對於我們,我們自然是希望執行的步驟越少越好:

  • 我們先從[tbl1]中挑出 col1在(5,6)範圍內的所有記錄
  • 對於每一條這樣的記錄,去遍歷 tbl2, 找出匹配,成為符合的輸出
  • 所以,如果第一個表,它滿足條件的記錄,越少,是不是我們在tbl2中遍歷,也就越少?因此一個大原則誕生了,我們一定希望,第一張表符合條件的越少越好。 這個原則會成為聯表優化最重要的原則

2.5.2 聯表優化實戰分析

SELECT tbl1.col1, tbl2.col2, tbl3.col3
FROM tbl1
INNER JOIN tbl2 USING(tbl1.col1)
INNER JOIN tbl3 USING(tbl3.col3);複製程式碼

  • tbl1 一共 1000行,tbl3 一共200行, 三張表都有索引
  • 雖然第一個出現在SQL語句裡的tbl1, 在經過聯表優化以後第一個出現的是tbl3,原因:
    • 如果我們拿著第一張表的索引前往第二張表匹配,根據索引查詢都很快
    • 所以問題變成了如果第一張表會越短,我們匹配的次數就越少
    • tbl3 比 tbl1 短,因此被自動優化成第一個出現的表



相關文章