Mysql進(jìn)階第五天

索引與執(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' ;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容