索引與執(zhí)行計(jì)劃
索引入門
索引是什么?
生活中的索引
MySQL 官方對(duì)索引的定義為:索引(Index)是幫助 MySQL 高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。可以得到索引的本質(zhì):索引是數(shù)據(jù)結(jié)構(gòu)。
上面的理解比較抽象,舉一個(gè)例子,平時(shí)看任何一本書,首先看到的都是目錄,通過目錄去查詢書籍里面的內(nèi)容會(huì)非常的迅速。

上圖就是一本金瓶梅的書,書籍的目錄是按順序放置的,有第一節(jié),第二節(jié)它本身就是一種順序存放的數(shù)據(jù)結(jié)構(gòu),是一種順序結(jié)構(gòu)。
另外通過目錄(索引),可以快速查詢到目錄里面的內(nèi)容,它能高效獲取數(shù)據(jù),通過這個(gè)簡(jiǎn)單的案例可以理解索引就是高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),再來看一個(gè)復(fù)雜的情況:

我們要去圖書館找一本書,這圖書館的書肯定不是線性存放的,它對(duì)不同的書籍內(nèi)容進(jìn)行了分類存放,整索引由于一個(gè)個(gè)節(jié)點(diǎn)組成,根節(jié)點(diǎn)有中間節(jié)點(diǎn),中間節(jié)點(diǎn)下面又由子節(jié)點(diǎn),最后一層是葉子節(jié)點(diǎn);
可見,整個(gè)索引結(jié)構(gòu)是一棵倒掛著的樹,其實(shí)它就是一種數(shù)據(jù)結(jié)構(gòu),這種數(shù)據(jù)結(jié)構(gòu)比前面講到的線性目錄更好的增加了查詢的速度。
MySql 中的索引

MySql 中的索引其實(shí)也是這么一回事,我們可以在數(shù)據(jù)庫(kù)中建立一系列的索引,比如創(chuàng)建主鍵的時(shí)候默認(rèn)會(huì)創(chuàng)建主鍵索引,上圖是一種 BTREE 的索引。每一個(gè)節(jié)點(diǎn)都是主鍵的 ID,當(dāng)我們通過 ID 來查詢內(nèi)容的時(shí)候,首先去查索引庫(kù),在到索引庫(kù)后能快速的定位索引的具體位置。
談下 B+Tree
要談 B+TREE 說白了還是 Tree,但談這些之前還要從基礎(chǔ)開始講起。
二分查找
二分查找法(binary search) 也稱為折半查找法,用來查找一組有序的記錄數(shù)組中的某一記錄。
其基本思想是:將記錄按有序化(遞增或遞減)排列,在查找過程中采用跳躍式方式查找,即先以有序數(shù)列的中點(diǎn)位置作為比較對(duì)象,如果要找的元素值小于該中點(diǎn)元素,則將待查序列縮小為左半部分,否則為右半部分。通過一次比較,將查找區(qū)間縮小一半。
# 給出一個(gè)例子,注意該例子已經(jīng)是升序排序的,且查找 數(shù)字 48
數(shù)據(jù):5, 10, 19, 21, 31, 37, 42, 48, 50, 52
下標(biāo):0, 1, 2, 3, 4, 5, 6, 7, 8, 9
? 步驟一:設(shè) low 為下標(biāo)最小值 0 , high 為下標(biāo)最大值 9 ;?
? 步驟二:通過 low 和 high 得到 mid ,mid=(low + high) / 2,初始時(shí) mid 為下標(biāo) 4 (也可以=5,看具體算法實(shí)現(xiàn));
? 步驟三 : mid=4 對(duì)應(yīng)的數(shù)據(jù)值是 31,31 < 48(我們要找的數(shù)字);
? 步驟四:通過二分查找的思路,將 low 設(shè)置為 31 對(duì)應(yīng)的下標(biāo) 4 , high 保持不變?yōu)?9 ,此時(shí) mid 為 6 ;
? 步驟五 : mid=6 對(duì)應(yīng)的數(shù)據(jù)值是 42,42 < 48(我們要找的數(shù)字);
? 步驟六:通過二分查找的思路,將 low 設(shè)置為 42 對(duì)應(yīng)的下標(biāo) 6 , high 保持不變?yōu)?9 ,此時(shí) mid 為 7 ;
? 步驟七 : mid=7 對(duì)應(yīng)的數(shù)據(jù)值是 48,48 == 48(我們要找的數(shù)字),查找結(jié)束;
通過 3 次 二分查找 就找到了我們所要的數(shù)字,而順序查找需 8 。
二叉樹(Binary Tree)
每個(gè)節(jié)點(diǎn)至多只有二棵子樹;
? 二叉樹的子樹有左右之分,次序不能顛倒;
? 一棵深度為 k,且有 (2的k次冪減1) 個(gè)節(jié)點(diǎn),稱為滿二叉樹(Full Tree);
? 一棵深度為 k,且 root 到 k-1 層的節(jié)點(diǎn)樹都達(dá)到最大,第 k 層的所有節(jié)點(diǎn)都 連續(xù)集中在最左邊,此時(shí)為完全二叉樹(Complete Tree)。

平衡二叉樹(AVL-樹)
? 左子樹和右子樹都是平衡二叉樹;
? 左子樹和右子樹的高度差絕對(duì)值不超過 1。
平衡二叉樹:

非平衡二叉樹:

平衡二叉樹的遍歷
? 前序 :6 ,3, 2, 5,7, 8(ROOT 節(jié)點(diǎn)在開頭, 中 -左-右 順序)
? 中序 :2, 3, 5, 6,7, 8(中序遍歷即為升序,左- 中 -右 順序)
? 后序 :2, 5, 3, 8,7, 6 (ROOT 節(jié)點(diǎn)在結(jié)尾,左-右- 中 順序)
平衡二叉樹的旋轉(zhuǎn)

需要通過旋轉(zhuǎn)(左旋,右旋)來維護(hù)平衡二叉樹的平衡,在添加和刪除的時(shí)候需要有額外的開銷。
B+Tree
B+樹的定義
? 數(shù)據(jù)只存儲(chǔ)在葉子節(jié)點(diǎn)上,非葉子節(jié)點(diǎn)只保存索引信息;
? 非葉子節(jié)點(diǎn)(索引節(jié)點(diǎn))存儲(chǔ)的只是一個(gè) Flag,不保存實(shí)際數(shù)據(jù)記錄;
? 索引節(jié)點(diǎn)指示該節(jié)點(diǎn)的左子樹比這個(gè) Flag 小,而右子樹大于等于這個(gè) Flag 。
? 葉子節(jié)點(diǎn)本身按照數(shù)據(jù)的升序排序進(jìn)行鏈接(串聯(lián)起來);
? 葉子節(jié)點(diǎn)中的數(shù)據(jù)在 物理存儲(chǔ)上是無序 的,僅僅是在 邏輯上有序 (通過指針串在一起)。
B+樹的作用
? 在塊設(shè)備上,通過 B+樹可以有效的存儲(chǔ)數(shù)據(jù);
? 所有記錄都存儲(chǔ)在葉子節(jié)點(diǎn)上,非葉子(non-leaf)存儲(chǔ)索引(keys)信息;
? B+樹含有非常高的扇出(fanout),通常超過 100,在查找一個(gè)記錄時(shí),可以有效的減少 IO 操作。
B+樹的扇出(fan out)

? 該 B+ 樹高度為 2
? 每葉子頁(yè)(LeafPage)4 條記錄
? 扇出數(shù)為 5
? 葉子節(jié)點(diǎn)(LeafPage)由小到大(有序)串聯(lián)在一起
扇出: 是每個(gè)索引節(jié)點(diǎn)(Non-LeafPage)指向每個(gè)葉子節(jié)點(diǎn)(LeafPage)的指針;
扇出數(shù) = 索引節(jié)點(diǎn)(Non-LeafPage)可存儲(chǔ)的最大關(guān)鍵字個(gè)數(shù) + 1;
圖例中的索引節(jié)點(diǎn)(Non-LeafPage)最大可以存放 4 個(gè)關(guān)鍵字,但實(shí)際使用了 3 個(gè)。
B+樹的插入操作
? B+樹的插入:B+樹的插入必須保證插入后葉子節(jié)點(diǎn)中的記錄依然排序。

問題:1 插入 28


問題 2:插入 70

問題 3:插入 95

要想更好的了解插入規(guī)則,這個(gè)鏈接里面寫得很詳細(xì):https://blog.csdn.net/shenchaohao12321/article/details/83243314?
索引的分類
普通索引:即一個(gè)索引只包含單個(gè)列,一個(gè)表可以有多個(gè)單列索引
唯一索引:索引列的值必須唯一,但允許有空值
復(fù)合索引:即一個(gè)索引包含多個(gè)列
聚簇索引(聚集索引):并不是一種單獨(dú)的索引類型,而是一種數(shù)據(jù)存儲(chǔ)方式。具體細(xì)節(jié)取決于不同的實(shí)現(xiàn),InnoDB 的聚簇索引其實(shí)就是在同一個(gè)結(jié)構(gòu)中保存了 B-Tree 索引(技術(shù)上來說是 B+Tree)和數(shù)據(jù)行。
非聚簇索引:不是聚簇索引,就是非聚簇索引
基礎(chǔ)語法
查看索引
SHOW INDEX FROM table_name\G
創(chuàng)建索引
CREATE [UNIQUE ] INDEX indexName ON mytable(columnname(length));
ALTER TABLE 表名 ADD [UNIQUE ] INDEX [indexName] ON (columnname(length));
刪除索引
DROP INDEX [indexName] ON mytable;
執(zhí)行計(jì)劃
什么是執(zhí)行計(jì)劃?
使用 EXPLAIN 關(guān)鍵字可以模擬優(yōu)化器執(zhí)行 SQL 查詢語句,從而知道 MySQL 是如何處理你的 SQL 語句的。分析你的查詢語句或是表結(jié)構(gòu)的性能瓶頸
?執(zhí)行計(jì)劃的作用
? 表的讀取順序
? 數(shù)據(jù)讀取操作的操作類型
? 哪些索引可以使用
? 哪些索引被實(shí)際使用
? 表之間的引用
? 每張表有多少行被優(yōu)化器查詢
以上的這些作用會(huì)在執(zhí)行計(jì)劃詳解里面介紹到,在這里不做解釋。
執(zhí)行計(jì)劃的語法
執(zhí)行計(jì)劃的語法其實(shí)非常簡(jiǎn)單: 在 SQL 查詢的前面加上 EXPLAIN 關(guān)鍵字就行。
比如:EXPLAIN select * from table1 ;
重點(diǎn)的就是 EXPLAIN 后面你要分析的 SQL 語句 。
執(zhí)行計(jì)劃詳解
通過 EXPLAIN 關(guān)鍵分析的結(jié)果由以下列組成,接下來挨個(gè)分析每一個(gè)列:

ID 列
ID 列:描述 select 查詢的序列號(hào),包含一組數(shù)字,表示查詢中執(zhí)行 select 子句或操作表的順序;
根據(jù) ID 的數(shù)值結(jié)果可以分成一下三種情況:
? id 相同:執(zhí)行順序由上至下
? id 不同:如果是子查詢,id 的序號(hào)會(huì)遞增,id 值越大優(yōu)先級(jí)越高,越先被執(zhí)行
? id 相同又不同:同時(shí)存在
分別舉例來看:
ID 相同:

如上圖所示,ID 列的值全為 1,代表執(zhí)行的允許從 t1 開始加載,依次為 t3 與 t2
EXPLAIN
select t2.* from t1,t2,t3 where t1.id = t2.id and t1.id = t3.id
and t1.other_column = ' ';
ID 不同:

如果是子查詢,id 的序號(hào)會(huì)遞增,id 值越大優(yōu)先級(jí)越高,越先被執(zhí)行;
EXPLAIN
select t2.* from t2 where id = (
select id from t1 where id = (select t3.id from t3 where t3.other_column=' ‘’));
ID 相同又不同:

id 如果相同,可以認(rèn)為是一組,從上往下順序執(zhí)行;
在所有組中,id 值越大,優(yōu)先級(jí)越高,越先執(zhí)行;
EXPLAIN
select t2.* from (
select t3.id
from t3 where t3.other_column = '' ) s1 ,t2 where s1.id = t2.id ;
select_type 列
Select_type: 查詢的類型,使用類型的區(qū)別:普通查詢、聯(lián)合查詢、子查詢等的復(fù)雜查詢;
類型如下:

SIMPLE
EXPLAIN select * from t1;
簡(jiǎn)單的 select 查詢,查詢中不包含子查詢或者 UNION;

PRIMARY 與 SUBQUERY
PRIMARY:查詢中若包含任何復(fù)雜的子部分,最外層查詢則被標(biāo)記為
SUBQUERY:在 SELECT 或 WHERE 列表中包含了子查詢
EXPLAIN
select t1.*,(select t2.id from t2 where t2.id = 1 ) from t1;

DERIVED
在 FROM 列表中包含的子查詢被標(biāo)記為 DERIVED(衍生);
MySQL 會(huì)遞歸執(zhí)行這些子查詢, 把結(jié)果放在臨時(shí)表里。
select t1.* from t1 ,(select t2.* from t2 where t2.id = 1 ) s2 where t1.id = s2.id ;

UNION RESULT 與 UNION
UNION:若第二個(gè) SELECT 出現(xiàn)在 UNION 之后,則被標(biāo)記為 UNION;
UNION RESULT:從 UNION 表獲取結(jié)果的 SELECT 。
#UNION RESULT ,UNION
EXPLAIN
select * from t2
UNION
select * from t2 ;

table 列
顯示這一行的數(shù)據(jù)是關(guān)于哪張表的:

Type 列
type 顯示的是訪問類型,是較為重要的一個(gè)指標(biāo),結(jié)果值從最好到最壞依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery >index_subquery > range > index > ALL 。
需要記憶的:
system>const>eq_ref>ref>range>index>ALL ;
一般來說,得保證查詢至少達(dá)到 range 級(jí)別,最好能達(dá)到 ref。
System 與 const
System:表只有一行記錄(等于系統(tǒng)表),這是 const 類型的特列,平時(shí)不會(huì)出現(xiàn),這個(gè)也可以忽略不計(jì);
Const:表示通過索引一次就找到了;
const 用于比較 primary key 或者 unique 索引。因?yàn)橹黄ヅ湟恍袛?shù)據(jù),所以很快;
如將主鍵置于 where 列表中,MySQL 就能將該查詢轉(zhuǎn)換為一個(gè)常量 。

EXPLAIN
SELECT * from (select * from t2 where id = 1) d1;
eq_ref
唯一性索引掃描,對(duì)于每個(gè)索引鍵,表中只有一條記錄與之匹配。常見于主鍵或唯一索引掃描;


EXPLAIN
SELECT * from t1,t2 where t1.id = t2.id ;
Ref
非唯一性索引掃描,返回匹配某個(gè)單獨(dú)值的所有行. 本質(zhì)上也是一種索引訪問,它返回所有匹配某個(gè)單獨(dú)值的行,然而,它可能會(huì)找到多個(gè)符合條件的行,所以他應(yīng)該屬于查找和掃描的混合體。

EXPLAIN
select count(DISTINCT col1) from t1 where col1 = 'ac' ;
或者
EXPLAIN
select col1 from t1 where col1 = 'ac' ;

Range
只檢索給定范圍的行,使用一個(gè)索引來選擇行。key 列顯示使用了哪個(gè)索引,一般就是在你的 where 語句中出現(xiàn)了 between、<、>、in 等的查詢。
這種范圍掃描索引掃描比全表掃描要好,因?yàn)樗恍枰_始于索引的某一點(diǎn),而結(jié)束語另一點(diǎn),不用掃描全部索引。

EXPLAIN select * from t1 where id BETWEEN 30 and 60 ;
EXPLAIN select * from t1 where id in(1,2) ;
Index
當(dāng)查詢的結(jié)果全為索引列的時(shí)候,雖然也是全部掃描,但是只查詢的索引庫(kù),而沒有去查詢數(shù)據(jù)。
EXPLAIN
select c2 from testdemo ;


All
Full Table Scan,將遍歷全表以找到匹配的行。

possible_keys 與 Key
possible_keys:可能使用的 key ;
Key:實(shí)際使用的索引。如果為 NULL,則沒有使用索引 ;
查詢中若使用了覆蓋索引,則該索引和查詢的 select 字段重疊 ;
這里的覆蓋索引非常重要,后面會(huì)單獨(dú)的來講 。


EXPLAIN select col1,col2 from t1 ;
其中 key 和 possible_keys 都可以出現(xiàn) null 的情況 。
key_len

EXPLAIN
select * from ta where col1 ='ab';
EXPLAIN
select * from ta where col1 ='ab' and col2 = 'ac' Key_len ;
表示索引中使用的字節(jié)數(shù),可通過該列計(jì)算查詢中使用的索引的長(zhǎng)度。在不損失精確性的情況下,長(zhǎng)度越短越好。
key_len 顯示的值為索引字段的最大可能長(zhǎng)度,并非實(shí)際使用長(zhǎng)度,即 key_len 是根據(jù)表定義計(jì)算而得,不是通過表內(nèi)檢索出的。

? key_len 表示索引使用的字節(jié)數(shù);
? 根據(jù)這個(gè)值,就可以判斷索引使用情況,特別是在組合索引的時(shí)候,判斷所有的索引字段是否都被查詢用到;
? char 和 varchar 跟字符編碼也有密切的聯(lián)系;
? latin1 占用 1 個(gè)字節(jié),gbk 占用 2 個(gè)字節(jié),utf8 占用 3 個(gè)字節(jié)。(不同字符編碼占用的存儲(chǔ)空間不同)。
字符類型

以上這個(gè)表列出了所有字符類型,但真正建所有的類型常用情況只是 CHAR、VARCHAR 。
字符類型-索引字段為 char 類型+不可為 Null 時(shí)

CREATE TABLE `s1` (
`id` int(11) NOT NULL AUTO_INCREMENT, `name` char(10) NOT NULL, `addr` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`), KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;
explain select * from s1 where name='enjoy';
name 這一列為 char(10),字符集為 utf-8 占用 3 個(gè)字節(jié);
Keylen=10*3 。
字符類型-索引字段為 char 類型+允許為 Null 時(shí)

CREATE TABLE `s2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` char(10) DEFAULT NULL, `addr` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`), KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
explain select * from s2 where name='enjoyedu';
name 這一列為 char(10),字符集為 utf-8 占用 3 個(gè)字節(jié),外加需要存入一個(gè) null 值;
Keylen=10*3+1(null) 結(jié)果為 31 。
索引字段為 varchar 類型+不可為 Null 時(shí)

CREATE TABLE `s3` (
`id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) NOT NULL, `addr` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`), KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
explain select * from s3 where name='enjoyeud';
Keylen=varchar(n)變長(zhǎng)字段+不允許 Null=n*(utf8=3,gbk=2,latin1=1)+2 。
索引字段為 varchar 類型+允許為 Null 時(shí)

CREATE TABLE `s3` (
`id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) NOT NULL, `addr` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`), KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
explain select * from s3 where name='enjoyeud';
Keylen=varchar(n)變長(zhǎng)字段+允許 Null=n*(utf8=3,gbk=2,latin1=1)+1(NULL)+2 。
數(shù)值類型

CREATE TABLE `numberKeyLen ` (
`c0` int(255) NOT NULL ,?
`c1` tinyint(255) NULL DEFAULT NULL ,?
`c2` smallint(255) NULL DEFAULT NULL ,?
`c3` mediumint(255) NULL DEFAULT NULL ,
?`c4` int(255) NULL DEFAULT NULL ,?
`c5` bigint(255) NULL DEFAULT NULL ,?
`c6` float(255,0) NULL DEFAULT NULL ,?
`c7` double(255,0) NULL DEFAULT NULL ,?
PRIMARY KEY (`c0`),?
INDEX `index_tinyint` (`c1`) USING BTREE ,?
INDEX `index_smallint` (`c2`) USING BTREE ,?
INDEX `index_mediumint` (`c3`) USING BTREE ,?
INDEX `index_int` (`c4`) USING BTREE ,?
INDEX `index_bigint` (`c5`) USING BTREE ,?
INDEX `index_float` (`c6`) USING BTREE ,?
INDEX `index_double` (`c7`) USING BTREE
)ENGINE=Innodb?
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT;
EXPLAIN
select * from numberKeyLen where c1=1
EXPLAIN
select * from numberKeyLen where c2=1
EXPLAIN
select * from numberKeyLen where c3=1
EXPLAIN
select * from numberKeyLen where c4=1
EXPLAIN
select * from numberKeyLen where c5=1
EXPLAIN
select * from numberKeyLen where c6=1
EXPLAIN
select * from numberKeyLen where c7=1
日期和時(shí)間

datetime 類型在 5.6 中字段長(zhǎng)度是 5 個(gè)字節(jié);
datetime 類型在 5.5 中字段長(zhǎng)度是 8 個(gè)字節(jié);
CREATE TABLE `datatimekeylen ` (
`c1` date NULL DEFAULT NULL ,?
`c2` time NULL DEFAULT NULL ,?
`c3` year NULL DEFAULT NULL ,?
`c4` datetime NULL DEFAULT NULL ,?
`c5` timestamp NULL DEFAULT NULL ,?
INDEX `index_date` (`c1`) USING BTREE ,?
INDEX `index_time` (`c2`) USING BTREE ,?
INDEX `index_year` (`c3`) USING BTREE ,?
INDEX `index_datetime` (`c4`) USING BTREE ,?
INDEX `index_timestamp` (`c5`) USING BTREE
)ENGINE=Innodb
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT;
EXPLAIN
SELECT * from datatimekeylen where c1 = 1
EXPLAIN
SELECT * from datatimekeylen where c2 = 1
EXPLAIN
SELECT * from datatimekeylen where c3 = 1
EXPLAIN
SELECT * from datatimekeylen where c4 = 1
EXPLAIN
SELECT * from datatimekeylen where c5 = 1
總結(jié)
字符類型
變長(zhǎng)字段需要額外的 2 個(gè)字節(jié)(VARCHAR 值保存時(shí)只保存需要的字符數(shù),另加一個(gè)字節(jié)來記錄長(zhǎng)度(如果列聲明的長(zhǎng)度超過 255,則使用兩個(gè)字節(jié)),所以 VARCAHR 索引長(zhǎng)度計(jì)算時(shí)候要加 2),固定長(zhǎng)度字段不需要額外的字節(jié)。
而 NULL 都需要 1 個(gè)字節(jié)的額外空間,所以索引字段最好不要為 NULL,因?yàn)?NULL 讓統(tǒng)計(jì)更加復(fù)雜并且需要額外的存儲(chǔ)空間。
復(fù)合索引有最左前綴的特性,如果復(fù)合索引能全部使用上,則是復(fù)合索引字段的索引長(zhǎng)度之和,這也可以用來判定復(fù)合索引是否部分使用,還是全部使用。
整數(shù)/浮點(diǎn)數(shù)/時(shí)間類型的索引長(zhǎng)度
NOT NULL=字段本身的字段長(zhǎng)度 ;
NULL=字段本身的字段長(zhǎng)度+1(因?yàn)樾枰惺欠駷榭盏臉?biāo)記,這個(gè)標(biāo)記需要占用 1個(gè)字節(jié)) ;
datetime 類型在 5.6 中字段長(zhǎng)度是 5 個(gè)字節(jié),datetime 類型在 5.5 中字段長(zhǎng)度是 8 個(gè)字節(jié) 。
Ref
顯示索引的哪一列被使用了,如果可能的話,是一個(gè)常數(shù)。哪些列或常量被用于查找索引列上的值 。

EXPLAIN
select * from s1 ,s2 where s1.id = s2.id and s1.name = 'enjoy' ;
由 key_len 可知 t1 表的 idx_col1_col2 被充分使用,col1 匹配 t2 表的 col1,col2 匹配了一個(gè)常量,即 'ac' 其中【shared.t2.col1】 為 【數(shù)據(jù)庫(kù).表.列】。
Rows
根據(jù)表統(tǒng)計(jì)信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數(shù)。

Extra
包含不適合在其他列中顯示但十分重要的額外信息。

Using filesort
說明 mysql 會(huì)對(duì)數(shù)據(jù)使用一個(gè)外部的索引排序,而不是按照表內(nèi)的索引順序進(jìn)行讀取。
MySQL 中無法利用索引完成的排序操作稱為“文件排序”,當(dāng)發(fā)現(xiàn)有 Using filesort 后,實(shí)際上就是發(fā)現(xiàn)了可以優(yōu)化的地方。

上圖其實(shí)是一種索引失效的情況,后面會(huì)講,可以看出查詢中用到了個(gè)聯(lián)合索引,索引分別為 col1,col2,col3;

當(dāng)我排序新增了個(gè) col2,發(fā)現(xiàn) using filesort 就沒有了。
EXPLAIN select col1 from t1 where col1='ac' order by col3 ;
EXPLAIN select col1 from t1 where col1='ac' order by col2,col3 ;
Using temporary
使了用臨時(shí)表保存中間結(jié)果,MySQL 在對(duì)查詢結(jié)果排序時(shí)使用臨時(shí)表。常見于排序 order by和分組查詢 group by。


尤其發(fā)現(xiàn)在執(zhí)行計(jì)劃里面有 using filesort 而且還有 Using temporary 的時(shí)候,特別需要注意。
EXPLAIN select col1 from t1 where col1 in('ac','ab','aa') GROUP BY col2 ;
EXPLAIN select col1 from t1 where col1 in('ac','ab','aa') GROUP BY col1,col2;
Using index
表示相應(yīng)的 select 操作中使用了覆蓋索引(Covering Index),避免訪問了表的數(shù)據(jù)行,效率不錯(cuò)!
如果同時(shí)出現(xiàn) using where,表明索引被用來執(zhí)行索引鍵值的查找;

如果沒有同時(shí)出現(xiàn) using where,表明索引用來讀取數(shù)據(jù)而非執(zhí)行查找動(dòng)作;

EXPLAIN select col2 from t1 where col1 = 'ab' ;
EXPLAIN select col2 from t1;
覆蓋索引
覆蓋索引(Covering Index),一說為索引覆蓋。
理解方式一:就是 select 的數(shù)據(jù)列只用從索引中就能夠取得,不必讀取數(shù)據(jù)行,MySQL 可以利用索引返回 select 列表中的字段,而不必根據(jù)索引再次讀取數(shù)據(jù)文件,換句話說查詢列要被所建的索引覆蓋。
理解方式二:索引是高效找到行的一個(gè)方法,但是一般數(shù)據(jù)庫(kù)也能使用索引找到一個(gè)列的數(shù)據(jù),因此它不必讀取整個(gè)行。畢竟索引葉子節(jié)點(diǎn)存儲(chǔ)了它們索引的數(shù)據(jù);當(dāng)能通過讀取索引就可以得到想要的數(shù)據(jù),那就不需要讀取行了。一個(gè)索引包含了(或覆蓋了)滿足查詢結(jié)果的數(shù)據(jù)就叫做覆蓋索引。
注意:
如果要使用覆蓋索引,一定要注意 select 列表中只取出需要的列,不可 select *,因?yàn)槿绻麑⑺凶侄我黄鹱鏊饕龝?huì)導(dǎo)致索引文件過大,查詢性能下降。
所以,千萬不能為了查詢而在所有列上都建立索引,會(huì)嚴(yán)重影響修改維護(hù)的性能。
Using where 與 using join buffer
Using where:表明使用了 where 過濾 ;
using join buffer:使用了連接緩存:show VARIABLES like '%join_buffer_size%';

EXPLAIN
select * from t1 JOIN t2 on t1.other_column = t2.other_column;
impossible where
where 子句的值總是 false,不能用來獲取任何元組 ;

EXPLAIN
select * from t1 where 1=2;
EXPLAIN
select * from t1 where t1.other_column ='enjoy' and t1.other_column = 'edu' ;