MySQL性能調優(3)查詢優化詳解

查詢執行路徑

##### MySQL 客戶端/服務端通信 - MySQL客戶端與服務端的通信方式是“半雙工”; - 客戶端一旦開始發送消息,另一端要接收完整個消息才能響應。客戶端一旦開始接收數據沒法停下來發送指令。 - 對於一個MySQL連接,或者說一個線程,時刻都有一個狀態來標識這個連接正在做什麼 - 查看命令 show full processlist / show processlist 我正在通過navicat向虛擬機裏面的數據庫導入數據 ``` Sleep 線程正在等待客戶端發送數據 Query 連接線程正在執行查詢 Locked 線程正在等待表鎖的釋放 Sorting result 線程正在對結果進行排序 Sending data 向請求端返回數據 可通過kill {id}的方式進行連接的殺掉 ``` [官網狀態全集](https://dev.mysql.com/doc/refman/8.0/en/general-thread-states.html)
查詢緩存
  • 前話:爲什麼MySQL默認關閉了緩存開啓??

MySQL 8.0不支持查詢緩存,用戶升級後將被鼓勵使用服務器端查詢重寫或ProxySQL作爲中間緩存。

下面是瞭解緩存應該知道的知識:
  • 工作原理:

緩存SELECT操作的結果集和SQL語句;
新的SELECT語句,先去查詢緩存,判斷是否存在可用的記錄集;

  • 判斷標準:

與緩存的SQL語句,是否完全一樣(包括語句內的空格),區分大小寫 (簡單認爲存儲了一個key-value結構,key爲sql,value爲sql查詢結果集)

  • 不會緩存的情況
    • 當查詢語句中有一些不確定的數據時,則不會被緩存。如包含函數NOW(),CURRENT_DATE()等類似的函數,或者用戶自定義的函數,存儲函數,用戶變量等都不會被緩存
    • 當查詢的結果大於query_cache_limit(查詢緩存返回行數)設置的值時,結果不會被緩存
    • 對於InnoDB引擎來說,當一個語句在事務中修改了某個表,那麼在這個事務提交之前,所有與這個表相關的查詢都無法被緩存。因此長時間執行事務,會大大降低緩存命中率
    • 查詢的表是系統表
    • 查詢語句不涉及到表
    • 當前表在之前做過其他增刪改,這個表中其他數據做的緩存都會失效、

    比如當前表中一行數據你做了緩存,表中其他數據更改了,也會影響這行數據的緩存失效。

查詢優化處理

  1. 解析sql

通過lex詞法分析,yacc語法分析將sql語句解析成解析樹學習連接

  1. 預處理階段

根據MySQL的語法的規則進一步檢查解析樹的合法性,如:檢查數據的表和列是否存在,解析名字和別名的設置。還會進行權限的驗證

  1. 查詢優化器

優化器的主要作用就是找到最優的執行計劃

如何找到最優執行計劃
  • 使用等價變化規則

5 = 5 and a > 5 改寫成 a > 5
a < b and a = 5 改寫成 b > 5 and a = 5
基於聯合索引,調整條件位置等

  • 優化count 、min、max等函數

min函數只需找索引最左邊(因爲innodb本身索引有序)
max函數只需找索引最右邊(因爲innodb本身索引有序)

myisam引擎count(*)

  • 覆蓋索引掃描

比如你查詢返回的字段命中了索引,他就會直接返回數據:這裏我給uname建立了索引

  • 子查詢優化(我們做優化的話:最好就是把子查詢改成連接查詢)

子查詢合併。即把多個子查詢合併成一個子查詢,作用是減少元組掃描的次數
子查詢反嵌套。也稱作子查詢上拉,即將子查詢重寫爲等價的多表連接
聚集子查詢消除。即聚集函數上推,將消除聚集函數的子查詢與父查詢做外連接

  • 提前終止查詢
    用了limit關鍵字(他就索引limit長度的數據)或者使用不存在的條件;
  • IN的優化

先進性排序,再採用二分查找的方式;
在條件多的情況下,or還是會一個個去掃描判斷,而in他會先找到中間的在判斷;再根據大小判斷。這樣就會減少多餘的掃描。

MySQL的查詢優化器是基於成本計算的原則。他會嘗試各種執行計劃。數據抽樣的方式進行試驗(隨機的讀取一個4K的數據塊進行分析)

查詢執行引擎

執行計劃順序

select查詢的序列號,標識執行的順序

1. id相同是相當於一組,執行順序由上至下
2. id不同,如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行
3. id相同又不同即兩種情況同時存在,id如果相同,可以認爲是一組,從上往下順序執行;在所有組中,id值越大,優先級越高,越先執行
查詢類型select_type
查詢的類型,主要是用於區分普通查詢、聯合查詢、子查詢等
SIMPLE:簡單的select查詢,查詢中不包含子查詢或者union
PRIMARY:查詢中包含子部分,最外層查詢則被標記爲primary
SUBQUERY/MATERIALIZED:SUBQUERY表示在select 或 where列表中包含了子查詢
MATERIALIZED表示where 後面in條件的子查詢
UNION:若第二個select出現在union之後,則被標記爲union
UNION RESULT:從union表獲取結果的select
執行計劃table

查詢涉及到的表,直接顯示錶名或者表的別名

<unionM,N> 由ID爲M,N 查詢union產生的結果
<subqueyN> 由ID爲N查詢生產的結果
執行計劃type

所以優化得至少在range及以上

訪問類型,sql查詢優化中一個很重要的指標,結果值從好到壞依次是:system > const > eq_ref > ref > range > index > ALL
system:表只有一行記錄(等於系統表),const類型的特例,基本不會出現,可以忽略不計
const:表示通過索引一次就找到了,const用於比較primary key 或者 unique索引
eq_ref:唯一索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵 或 唯一索引掃描
ref:非唯一性索引掃描,返回匹配某個單獨值的所有行,本質是也是一種索引訪問
range:只檢索給定<font color=red>範圍</font>的行,使用一個索引來選擇行
index:Full Index Scan,索引全表掃描,把索引從頭到尾掃一遍
ALL:Full Table Scan,遍歷全表以找到匹配的行
其他關鍵字
possible_keys:查詢過程中有可能用到的索引
key:實際使用的索引,如果爲NULL,則沒有使用索引 
rows:根據表統計信息或者索引選用情況,大致估算出找到所需的記錄所需要讀取的行數
filtered:它指返回結果的行佔需要讀到的行(rows列的值)的百分比表示返回結果的行數佔需讀取行數的百分比,filtered的值越大越好
額外信息Extra
  1. Using filesort :

MySQL對數據使用一個外部的文件內容進行了排序,而不是按照表內的索引進行排序讀取

  1. Using temporary:

使用臨時表保存中間結果,也就是說MySQL在對查詢結果排序時使用了臨時表,常見於order by 或 group by但是innodb用id排序分組是不會出現臨時表的。

  1. Using index:

表示相應的select操作中使用了覆蓋索引(Covering Index),避免了訪問表的數據行,效率高

  1. Using where :

使用了where過濾條件

  1. select tables optimized away:

基於索引優化MIN/MAX操作或者MyISAM存儲引擎優化COUNT(*)操作,不必等到執行階段在進行計算,查詢執行計劃生成的階段即可完成優化

分析一個組合查詢
``` 這條語句就是把兩個子查詢查出來再結合 這裏有個特殊情況:id等於null那個是最後執行的,因爲他需要的到其他查詢結果結合。 //-------------------------------- 1. 這裏就看id最大的4:select_type是subquery說明是子查詢;type爲ref非唯一性索引掃描;用到了id和address索引。 2. id爲3的開始執行,select_type爲union說明第二個select出現在union之後,則被標記爲union; 3. id等於2和id等於1就是之前的子查詢與查詢;並且用到了主鍵索引。 4. 然後最後再把1和3合併;這裏可以看出組合查詢使用到了臨時表。 ``` #### 執行引擎 調用插件式的存儲引擎的原子API的功能進行執行計劃的執行 #### 返回客戶端 增量的返回結果: > 開始生成第一條結果時,MySQL就開始往請求方逐步返回數據 > 好處: MySQL服務器無須保存過多的數據,浪費內存用戶體驗好,馬上就拿到了數據

慢查詢日誌

// 查看是否打開慢查詢日誌
show variables like 'slow_query_log'
// 打開慢查詢日誌
set global slow_query_log = on
//  保存日誌位置
set global slow_query_log_file ='/var/lib/mysql/gupaoedu-slow.log'
 // 沒有命中索引的都保存
set global log_queries_not_using_indexes = on
// 超過多少秒記錄日誌
set global long_query_time = 0.1 (秒)
//查看是否開啓
show variables like 'slow_query%';
慢日誌文件查看
Time :日誌記錄的時間
User@Host:執行的用戶及主機
Query_time:查詢耗費時間 Lock_time 鎖表時間 Rows_sent發送給請求方的記錄條數 Rows_examined 語句掃描的記錄條數
SET timestamp 語句執行的時間點
select .... 執行的具體語句
MySQL自帶的分析工具
mysqldumpslow -t 10 -s at /var/lib/mysql/gupaoedu-slow.log
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章