Mysql效能優化總結-04執行計劃
1.建測試表
教師表 DROP TABLE IF EXISTS `teacher`; CREATE TABLE `teacher` ( `tid` int(3) DEFAULT NULL, `tname` varchar(20) DEFAULT NULL, `tcid` int(3) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 插入資料 INSERT INTO `teacher` VALUES ('1', 'mashibing', '1'); INSERT INTO `teacher` VALUES ('2', 'james', '2'); INSERT INTO `teacher` VALUES ('3', 'lewis', '3');
課程表 DROP TABLE IF EXISTS `course`; CREATE TABLE `course` ( `cid` int(3) DEFAULT NULL, `cname` varchar(20) DEFAULT NULL, `tid` int(3) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 插入資料 INSERT INTO `course` VALUES ('1', 'mysql', '1'); INSERT INTO `course` VALUES ('2', 'jvm', '1'); INSERT INTO `course` VALUES ('3', 'java', '2'); INSERT INTO `course` VALUES ('4', 'spring', '3');
教師聯絡表 DROP TABLE IF EXISTS `teacher_contact`; CREATE TABLE `teacher_contact` ( `tcid` int(3) DEFAULT NULL, `phone` varchar(20) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; 插入資料 INSERT INTO `teacher_contact` VALUES ('1', '15888888888'); INSERT INTO `teacher_contact` VALUES ('2', '18888888888'); INSERT INTO `teacher_contact` VALUES ('3', '19999999999');
2.explain分析sql
id 是查詢序列編號,每張表都是單獨訪問的,一個select就會有一個序號。
id值不同的時候:
id值不同的時候,先查詢id值大的(先大後小)。
-- 查詢mysql課程的老師的手機號
EXPLAIN extended select
tc.phone
from teacher_contact tc
where tcid=(
select tcid from teacher t
where t.tid=(
select c.tid FROM course c
where c.cname='mysql'
)
)
執行計劃:
查詢順序:cource c ——teacher t——teacher_concact tc
先查課程表,再查教師表,最後查教師聯絡表。子查詢只能以這種方式查詢,拿到內層結果後才能進行外層查詢。
id值相同的時候:
--查詢課程ID為2,或者聯絡表id為3的老師
EXPLAIN extended
select t.tname,c.cname,tc.phone
from teacher t ,course c ,teacher_contact tc
where t.tid=c.cid
and t.tcid=tc.tcid
and (c.cid=2 or tc.tcid=3)
執行計劃:
id值相同時,表的查詢順序是從上往下順序執行。例如這次查詢的id都是1(說明子查詢被優化器轉換成了連線查詢),查詢的順序是teacher t(3條),teacher_contact tc(3條),course c(4條)。
在連線查詢中,先查詢的叫做驅動表,後查詢的叫做被驅動表。
應該把小表放在前面查詢,因為他的中間結果最少(小表驅動大表思想)。
id既有相同,也有不同
就是id不同的先大後小,id相同的從上往下。
3.explain詳解
1.select type
列舉一些常見的查詢型別
SIMPLE
簡單查詢,不包含子查詢,不包含union。
EXPLAIN SELECT * FROM teacher;
包含子查詢的案例
-- 查詢mysql課程的老師的手機號
EXPLAIN extended select
tc.phone
from teacher_contact tc
where tcid=(
select tcid from teacher t
where t.tid=(
select c.tid FROM course c
where c.cname='mysql'
)
)
PAIMARY
子查詢sql 語句中的主查詢,也就是最外面的那層查詢。
SUBQUERY
子查詢中所有的內層查詢都是SUBQUERY型別。
DERIVED
衍生查詢,表示在得到最終查詢結果前,會用到臨時表。例如:
這裡先執行了UNION查詢,後進行了DERIVED查詢。
UNION
用到了UNION查詢,同上。
UNION RESULT
顯示哪些表之間存在UNION查詢。<union2,3>代表course表id為2,3的查詢存在UNION。同上例。
這裡沒有列舉全部的select_type,還有dependent union、dependent subquery、materialized、uncacheable subquery、uncacheable union。
2.TYPE連線型別
連線型別常見連線型別效能排序:system>const>rq_ref>ref>range>index>all。
這裡沒列舉全,還有fulltext、ref_or_null、index_merge、unique_subquery、index_subquery。
以上連線型別除了all都能用到索引。
型別 | 出現條件 |
---|---|
const | 主鍵索引或者唯一索引,只能查到一條資料的sql。 |
system | system是const的一種特例,只有一行滿足條件,對於MyISAM,Memory的表,只查詢到一條記錄,也是system。 |
eq_ref
通常出現在多表關聯查詢,被驅動表通過唯一性索引(unique或primary key)進行訪問,此時被驅動表的訪問方式就是eq_ref。
eq_ref是除const外最好的訪問型別。
teacher_contact tcid欄位上加上主鍵,
執行sql
EXPLAIN extended
select t.tcid
from teacher t,teacher_contact tc
where t.tcid=tc.tcid
計劃如下:
小結:
以上3種,system,const,eq_ref,都是可遇不可求的,很難優化到這個狀態。
ref
查詢用到了非唯一索引,或者關聯操作只使用了索引最左字首。
teacher表tcid欄位加索引,然後執行sql
EXPLAIN extended
select t.tcid
from teacher t
where t.tcid=2
range
索引範圍掃描。
如果where後面是between and 或 < 或 >或 <= 或 >=或in這些,type型別就為range。
teacher 表tid加索引,執行
EXPLAIN extended
select t.tcid
from teacher t
where t.tid < 3
&emspin查詢也可能是range。這裡不舉例子(PS:我這裡試過了是index)
index
執行sql
&emspteacher; 表tid欄位有索引。
EXPLAIN extended
select t.tid
from teacher t
full index scan,索引全掃描,比不走索引快。
all
full table scan,全表掃描,如果沒有索引或者沒用到索引,type就是index。
NULL
不用訪問表或者索引就能得到結果,例如:
explain select 1 from dual where 1=1;
小結:
一般來說,需要保證查詢的type至少達到range級別,最好能達到 ref。
all全表掃描,和index 都是可以優化的,視情況而定,和資料量有關係,資料量影響優化器的選擇,優化器會選擇成本低的執行計劃。