在工作中如果遇到慢sql通常都可以用explain進行解析。
先列一下各個列名以及含義
列名 | 描述 |
---|---|
id | 在一個大的查詢中每一個查詢關鍵字都對應一個id |
select type | select關鍵字對應的那個查詢類型 |
table | 表名 |
partitions(*) | 分配的分區信息 |
type | 針對單表的訪問方法 |
possible_keys | 可能用到的索引 |
key | 實際上使用的索引 |
key len | 實際用到的索引長度 |
ref | 當索引列等值查詢時,與索引列進行等值匹配的對象信息 |
rows | 預估的需要讀取的記錄條數 |
filtered | 某個表經過搜索條件過濾后剩余記錄條數的百分比 |
extra | 一些額外的信息 |
id
一個查詢語句就會有一個對應的id,如果其內部包含子查詢且沒有被查詢優化器優化掉的情況下就會出現不同的id……
select type
-
primary 主查詢 (出現子查詢的語句時會區分子和主查詢)
-
subquery (非相關子查詢)
非相關子查詢得到的結果表會被物化,只需要執行一遍
-
dependent query(相關子查詢)
相關子查詢可能會被執行多次嗷
-
union 聯查時
-
union result 臨時表
-
simple 簡單查詢
-
derived派生表
出現在有子查詢時,如果為該類型則代表該查詢是以物化的方式執行的
-
materialized
當子查詢物化后與外層查詢進行連接時的查詢類型。
type
-
system
innodb中不存在,MyISAM、Memory引擎中有,代表精確的查詢
-
const
主鍵或者唯一二級索引時的常量查詢
例如
where a=1
,a為主鍵 -
eq_ref
代表連接查詢時,被驅動表是通過逐漸或者唯一二級索引列等值匹配的方式進行訪問的
-
ref
非主鍵或者唯一索引時使用索引的查詢
-
ref or null
ref的情況+條件中出現null
-
index merge
索引合並查詢,同時使用了多個索引的情況。
-
unique subquery
通常出現在相關子查詢把in優化為exists而且子查詢可以使用主鍵進行查找時
-
index subquery
與unique類似,但訪問的是普通的索引
-
range
范圍查詢時出現
-
index
查詢輔助索引字段時出現,遍歷輔助索引值。
-
all
全表掃描
key lenth
當前使用索引字段的長度
如果索引值可以為空,key length會多存儲一個字節
如果為變長字段(例如varchar),需要2個字節的存儲空間存儲長度。
ref
代表驅動查詢的字段。
例如在相關子查詢中,子查詢的驅動字段應該為主查詢中表的某個值。
filtered
通過該索引查詢到的數據滿足 非索引條件的數據所占的百分比。
select * from table where index_case and non_index_case;
假設符合index_case 的值為100個(rows=100),但是符合non_index_case的值為20個,那么filtered就為20。
注:為估算值。
extra
Extra列是用來說明一些額外信息的,我們可以通過這些額外信息來更准確的理解MySQL到底將如何執行給定的
查詢語句。
-
no tables used
當查詢語句的沒有FROM子句時將會提示該額外信息。
-
impossible where
where子句永遠為false
-
no matching min/max row
查詢列表中有min或者max聚集函數,但是並沒有where子句中的搜索條件記錄時會提示該額外信息
-
using index
查詢列表以及搜索條件中只包含屬於某個索引的列,既索引覆蓋
-
using index condition
搜索條件中雖然出現了索引列,但是有部分條件無法使用索引,會根據能用索引的條件先搜索一遍再匹配無法使用索引的條件
-
using where
全表掃描並且where中有針對該表的搜索條件
-
using join buffer(Block Nested Loop)
在連接查詢執行過程中,當被驅動表不能有效的利用索引加快訪問速度時就分配一塊join buffer內存塊來加快查詢速度。
-
using filesort
多數情況下排序操作無法用到索引,只能在內存中(記錄較少時)或者磁盤中進行排序,這種在情況統稱為文件排序。
-
using temporary
在諸多查詢過程中,可能會借助臨時表來完成一些查詢功能,比如去重、排序之類的,比如我們在執行許多包含distinct、group by、union等子句的查詢過程中,如果不能有效利用索引完成查詢,mysql可能通過建立內部臨時表來執行查詢。
-
Start temporary, End temporary
子查詢可以優化成半連接,但通過了臨時表進行去重
-
firstmatch(table_name)
子查詢時可以優化成半連接,但直接進行數據比較去重
index hint
use index
select * from table use index (index_name,index_name2) where case;
強制查詢優化器在指定的索引中做選擇。
force index
select * from table force index (index_name) where case;
強制查詢優化器使用該索引
ignore index
select * from ignore index (index_name) where case;
強制忽略該索引。
小結
性能按照type排序
system > const > eq_ref > ref > ref_or_null > index_merge > unique_subquery > index_subquery > range >index > ALL
性能按照extra排序
- Using index:用了覆蓋索引
- Using index condition:用了條件索引(索引下推)
- Using where:從索引查出來數據后繼續用where條件過濾
- Using join buffer (Block Nested Loop):join的時候利用了join buffer(優化策略:去除外連接、增大join buffer大小)
- Using filesort:用了文件排序,排序的時候沒有用到索引
- Using temporary:用了臨時表(優化策略:增加條件以減少結果集、增加索引,思路就是要么減少待排序的數量,要么提前排好序)
- Start temporary, End temporary:子查詢的時候,可以優化成半連接,但是使用的是通過臨時表來去重
- FirstMatch(tbl_name):子查詢的時候,可以優化成半連接,但是使用的是直接進行數據比較來去重
常見優化手段
- SQL語句中IN包含的值不應過多,不能超過200個,200個以內查詢優化器計算成本時比較精准,超過200個是估算的成本,另外建議能用between就不要用in,這樣就可以使用range索引了。
- SELECT語句務必指明字段名稱:SELECT * 增加很多不必要的消耗(cpu、io、內存、網絡帶寬);增加
了使用覆蓋索引的可能性;當表結構發生改變時,前斷也需要更新。所以要求直接在select后面接上字段
名。 - 當只需要一條數據的時候,使用limit 1
- 排序時注意是否能用到索引
- 使用or時如果沒有用到索引,可以改為union all 或者union
- 如果in不能用到索引,可以改成exists看是否能用到索引
- 使用合理的分頁方式以提高分頁的效率
- 不建議使用%前綴模糊查詢
- 避免在where子句中對字段進行表達式操作
- 避免隱式類型轉換
- 對於聯合索引來說,要遵守最左前綴法則
- 必要時可以使用force index來強制查詢走某個索引
- 對於聯合索引來說,如果存在范圍查詢,比如between,>,<等條件時,會造成后面的索引字段失效。
- 盡量使用inner join,避免left join,讓查詢優化器來自動選擇小表作為驅動表
- 必要時刻可以使用straight_join來指定驅動表,前提條件是本身是inner join