有時候咱們會遇到這樣的事情:項目上線了,一切順利,就等產品驗收完畢回家睡覺了,可是產品忽然來找你了:mysql
產品:首頁爲何加載這麼慢算法
開發:數據量太大了,沒辦法sql
產品:不行,必須優化後端
開發:....緩存
這種狀況,很大機率是sql出現慢查詢了,此時咱們就須要把查詢sql拉出來優化優化了,那咱們就須要使用到本文要說的explain命令了。bash
在瞭解explain以前,不妨先看下mysql服務大體的邏輯架構圖,以對其有一個總體的認識服務器
從圖中能夠看出,咱們的sql在查詢的時候主要須要經歷如下步驟:架構
而咱們使用explain便是去查詢優化器查詢執行計劃框架
看一條簡單的執行計劃性能
explain select * from t_user where id = 1;
複製代碼
咱們能夠看到,一個執行計劃會展現12個相關的字段,下面咱們對主要字段以及這些字段常見的值進行解釋:
含義:是一組數字,表示的是查詢中執行select子句或者是操做表的順序
規則:
常見的值以及描述以下
值 | 描述 |
---|---|
SIMPLE | 簡單的SELECT語句(不包括UNION操做或子查詢操做) |
PRIMARY | 查詢中最外層的SELECT(如兩表作UNION或者存在子查詢的外層的表操做爲PRIMARY,內層的操做爲UNION) |
UNION | UNION操做中,查詢中處於內層的SELECT,即被union的SELECT |
SUBQUERY | 子查詢中的SELECT |
DERIVED | 表示包含在 From 子句中的 Select 查詢 |
UNION RESULT | union的結果,此時id爲NULL |
涉及的表
這列很重要,顯示了鏈接使用哪一種類型,有無使用索引, 常見的值從最好到最差以下 system > const > eq_ref > ref > range > index > all
各值的描述以下
值 | 描述 |
---|---|
system | 表只有一行,MyISAM引擎全部 |
const | 常量鏈接,表最多隻有一行匹配,一般用於主鍵或者惟一索引比較時,如: select * from t_user where id = 1; |
eq_ref | 表關聯查詢時,對於前表的每一行,後表只有一行與之匹配。 (1) join查詢 (2) 命中主鍵或者非空惟一索引 |
ref | 只使用了索引的最左前綴或者使用的索引是非惟一索引、非主鍵索引 |
range | between,in,>等都是典型的範圍(range)查詢 |
index | 須要掃描索引上的所有數據,如: select count(*) from t_user; |
all | 全表掃描 |
表示可能用到的索引
表示最終用到的key
顯示索引的哪一列被使用了,有時候會是一個常量:表示哪些列或常量被用於查找索引列上的值
估算出結果集行數,表示MySQL根據表統計信息及索引選用狀況,估算的找到所需的記錄所須要讀取的行數, 原則上 rows 越少越好。
查詢結果的行數占上面rows的百分比
這一列也很重要,主要展現額外的信息說明,可以給出讓咱們深刻理解執行計劃進一步的細節信息
常見的值及描述以下
值 | 描述 |
---|---|
Using filesort | 當order by 沒法利用索引完成排序時,優化器不得不選擇合適的算法從內存或者磁盤進行排序 |
Using temporary | 使用了臨時表 |
Using index | select後面的查詢字段在索引中就能夠取到,無需再回表了,即所謂的覆蓋索引,這種查詢性能很好 |
Using index condition | mysql5.6以後引入了ICP(索引條件下推) |
Using where | Mysql 服務器在存儲引擎檢索行後再進行過濾 |
一般有如下幾種優化原則:
讓主要查詢語句使用到合適的索引,type出現ALL(全表掃描)需格外注意,同時創建合適的索引以減小possible_keys的數量
type最好能達到ref級別
Extra列出現Using temporary、Using filesort(文件排序)務必去除
針對上面提到的幾點優化原則,提供以下的優化思路
上述1,2點其實均可以經過優化索引來達到目的,而要想讓咱們建的索引達到最優,則須要依據一個原則: 三星索引原則
簡單描述就是
☆: where後條件匹配的索引列越多掃描的數據將越少
好比組合索引(a,b,c),最好在where後面能同時用到索引上的a,b,c這三列
☆: 避免再次排序
簡單來講,就是排序字段儘可能使用索引字段,由於索引默認是排好序的,使用索引字段排序能夠避免再次排序
☆: 索引行包含查詢語句中全部的列,即覆蓋索引
基於這一點,咱們應該少用select*來查詢,以增長覆蓋索引的可能性
若是你的索引能集齊上述三顆星,則說明你的索引是最優的索引!
咱們建立以下表,並插入一些數據
用戶表
CREATE TABLE `t_user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`group_id` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_name`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1240277101395107842 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
複製代碼
分組表
CREATE TABLE `t_group` (
`id` bigint(20) NOT NULL,
`group_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;
複製代碼
order by 的字段不在where條件中
下面這條sql會出現Using filesort
select * from t_user where group_id = 2 and age = 32 order by name;
複製代碼
可是下面這條sql不會
select * from t_user where group_id = 2 and age = 32 order by group_id ;
複製代碼
組合索引跨列
舉例:給t_user表建立索引(name,age,group_id)
下面這條sql排序會出現Using filesort
select * from t_user where name= '李A' order by group_id;
複製代碼
可是下面這條就不會
select * from t_user where name = '李A' order by age;
複製代碼
由於第一條查詢order by跳過了age,直接使用了group_id;刪除索引(name,age,group_id);
因爲group by第一步默認進行了排序,因此當group by 的字段知足上述條件是,也會出現Using filesort,能夠在group by後面加上order by null取消排序
臨時表的出現對性能影響是很大的,主要會出如今如下狀況中
分組字段不在where條件後面,而且group by字段不是最終使用到的索引,緣由有點相似於上面的Using filesort
下面這條sql會出現Using temporary
select * from t_user where group_id = 2 and name= '李A' group by age;
複製代碼
可是下面這條sql不會
select * from t_user where name = '李A' and age = 21 group by age;
複製代碼
結論: where哪些字段,就group by 哪些字段
錶鏈接中,order by的列不是驅動表中的
以下sql是會建立臨時表的
explain select * from t_user t1 left join t_group t2 on t1.group_id = t2.id order by t2.id;
複製代碼
由於t1和t2鏈接的時候,t1是驅動表,可是排序使用了被驅動表t2中的字段。改成t1的字段排序就不會出現臨時表了,這裏就不舉例了。
結論: 鏈接查詢的時候,排序字段使用驅動表的字段
order by和group by的子句不同時
explain select * from t_user group by group_id order by `name`;
複製代碼
這種狀況只能儘可能使用同一個字段來分組和排序了,不然沒法避免
distinct查詢而且加上order by時
explain select DISTINCT(`name`) from t_user order by age;
複製代碼
這種狀況有時候沒法避免,只能儘可能將distinct的字段和order by的字段使用相同的索引。還有會出現臨時表的狀況有: from 中的子查詢、union,這裏就不一一舉例了。
sql優化已是咱們後端開發的內化技能之一了,在學習框架,設計思想的同時,不要忘記打牢基礎,但願各位可以有所收穫。