索引的概述
索引是一種用于快速查詢和檢索數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu),其本質(zhì)可以看成是一種排序好的數(shù)據(jù)結(jié)構(gòu)。索引的作用就相當(dāng)于書(shū)的目錄。打個(gè)比方: 我們?cè)诓樽值涞臅r(shí)候,如果沒(méi)有目錄,那我們就只能一頁(yè)一頁(yè)的去找我們需要查的那個(gè)字,速度很慢。如果有目錄了,我們只需要先去目錄里查找字的位置,然后直接翻到那一頁(yè)就行了。索引底層數(shù)據(jù)結(jié)構(gòu)存在很多種類型,常見(jiàn)的索引結(jié)構(gòu)有: B 樹(shù), B+樹(shù) 和 Hash、紅黑樹(shù)。在 MySQL 中,無(wú)論是 Innodb 還是 MyIsam,都使用了 B+樹(shù)作為索引結(jié)構(gòu)。
2、索引原理
索引的存儲(chǔ)原理大致可以概括為一句話:以空間換時(shí)間。
一般來(lái)說(shuō)索引本身也很大,不可能全部存儲(chǔ)在內(nèi)存中,因此索引往往是存儲(chǔ)在磁盤上的文件中的(可能存儲(chǔ)在單獨(dú)的索引文件中,也可能和數(shù)據(jù)一起存儲(chǔ)在數(shù)據(jù)文件中)。數(shù)據(jù)庫(kù)在未添加索引進(jìn)行查詢的時(shí)候默認(rèn)是進(jìn)行全文搜索,也就是說(shuō)有多少數(shù)據(jù)就進(jìn)行多少次查詢,然后找到相應(yīng)的數(shù)據(jù)就把它們放到結(jié)果集中,直到全文掃描完畢。
3、索引的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
大大提高數(shù)據(jù)查詢速度。
可以提高數(shù)據(jù)檢索的效率,降低數(shù)據(jù)庫(kù)的IO成本,類似于書(shū)的目錄。
通過(guò)索引列對(duì)數(shù)據(jù)進(jìn)行排序,降低數(shù)據(jù)的排序成本降低了CPU的消耗。
被索引的列會(huì)自動(dòng)進(jìn)行排序,包括【單例索引】和【組合索引】,只是組合索引的排序需要復(fù)雜一些。
如果按照索引列的順序進(jìn)行排序,對(duì)order by語(yǔ)句來(lái)說(shuō),效率就會(huì)提高很多。
缺點(diǎn):
索引會(huì)占據(jù)磁盤空間。
索引雖然會(huì)提高查詢效率,但是會(huì)降低更新表的效率。比如每次對(duì)表進(jìn)行增刪改查操作,MySQL不僅要保存數(shù)據(jù),還有保存或者更新對(duì)應(yīng)的索引文件。
維護(hù)索引需要消耗數(shù)據(jù)庫(kù)資源。
注意:但是,使用索引一定能提高查詢性能嗎? 大多數(shù)情況下,索引查詢都是比全表掃描要快的。但是如果數(shù)據(jù)庫(kù)的數(shù)據(jù)量不大,
那么使用索引也不一定能夠帶來(lái)很大提升。
4、索引的數(shù)據(jù)結(jié)構(gòu)
MySQL 默認(rèn)的存儲(chǔ)引擎 InnoDB 采用的是 B+ 作為索引的數(shù)據(jù)結(jié)構(gòu),原因有:
B+ 樹(shù)的非葉子節(jié)點(diǎn)不存放實(shí)際的記錄數(shù)據(jù),僅存放索引,因此數(shù)據(jù)量相同的情況下,相比存儲(chǔ)即存索引又存記錄的 B 樹(shù),B+樹(shù)的非葉子節(jié)點(diǎn)可以存放更多的索引,因此 B+ 樹(shù)可以比 B 樹(shù)更「矮胖」,查詢底層節(jié)點(diǎn)的磁盤 I/O次數(shù)會(huì)更少。
B+ 樹(shù)有大量的冗余節(jié)點(diǎn)(所有非葉子節(jié)點(diǎn)都是冗余索引),這些冗余索引讓 B+ 樹(shù)在插入、刪除的效率都更高,比如刪除根節(jié)點(diǎn)的時(shí)候,不會(huì)像 B 樹(shù)那樣會(huì)發(fā)生復(fù)雜的樹(shù)的變化;
B+ 樹(shù)葉子節(jié)點(diǎn)之間用鏈表連接了起來(lái),有利于范圍查詢,而 B 樹(shù)要實(shí)現(xiàn)范圍查詢,因此只能通過(guò)樹(shù)的遍歷來(lái)完成范圍查詢,這會(huì)涉及多個(gè)節(jié)點(diǎn)的磁盤 I/O 操作,范圍查詢效率不如 B+ 樹(shù)。
5、索引的分類
(1)我們可以按照四個(gè)角度來(lái)分類索引。
按「數(shù)據(jù)結(jié)構(gòu)」分類:B+tree索引、Hash索引、Full-text索引。
按「物理存儲(chǔ)」分類:聚簇索引(主鍵索引)、二級(jí)索引(輔助索引)。
按「字段特性」分類:主鍵索引、唯一索引、普通索引、前綴索引。
按「字段個(gè)數(shù)」分類:?jiǎn)瘟兴饕⒙?lián)合索引。
InnoDB 是在 MySQL 5.5 之后成為默認(rèn)的 MySQL 存儲(chǔ)引擎,B+Tree 索引類型也是 MySQL 存儲(chǔ)引擎采用最多的索引類型。
在創(chuàng)建表時(shí),InnoDB 存儲(chǔ)引擎會(huì)根據(jù)不同的場(chǎng)景選擇不同的列作為索引:
如果有主鍵,默認(rèn)會(huì)使用主鍵作為聚簇索引的索引鍵(key);
如果沒(méi)有主鍵,就選擇第一個(gè)不包含 NULL 值的唯一列作為聚簇索引的索引鍵(key);
在上面兩個(gè)都沒(méi)有的情況下,InnoDB 將自動(dòng)生成一個(gè)隱式自增 id 列作為聚簇索引的索引鍵(key);
其它索引都屬于輔助索引(Secondary Index),也被稱為二級(jí)索引或非聚簇索引。創(chuàng)建的主鍵索引和二級(jí)索引默認(rèn)使用的是 B+Tree 索引。
從物理存儲(chǔ)的角度來(lái)看,索引分為聚簇索引(主鍵索引)、二級(jí)索引(輔助索引)。
區(qū)別:
主鍵索引的 B+Tree 的葉子節(jié)點(diǎn)存放的是實(shí)際數(shù)據(jù),所有完整的用戶記錄都存放在主鍵索引的 B+Tree 的葉子節(jié)點(diǎn)里;
二級(jí)索引的 B+Tree 的葉子節(jié)點(diǎn)存放的是主鍵值,而不是實(shí)際數(shù)據(jù)。
所以,在查詢時(shí)使用了二級(jí)索引,如果查詢的數(shù)據(jù)能在二級(jí)索引里查詢的到,那么就不需要回表,這個(gè)過(guò)程就是覆蓋索引。
如果查詢的數(shù)據(jù)不在二級(jí)索引里,就會(huì)先檢索二級(jí)索引,找到對(duì)應(yīng)的葉子節(jié)點(diǎn),獲取到主鍵值后,然后再檢索主鍵索引,
就能查詢到數(shù)據(jù)了,這個(gè)過(guò)程就是回表。
(2)常見(jiàn)索引的介紹
1.主鍵索引:primary key
設(shè)定為主鍵后,數(shù)據(jù)庫(kù)自動(dòng)建立索引,InnoDB為聚簇索引,主鍵索引列值不能為空(Null)。
2.唯一索引:
索引列的值必須唯一,但允許有空值(Null),但只允許有一個(gè)空值(Null)。
3.復(fù)合索引:
一個(gè)索引可以包含多個(gè)列,多個(gè)列共同構(gòu)成一個(gè)復(fù)合索引。
4.全文索引:
Full Text(MySQL5.7之前,只有MYISAM存儲(chǔ)引擎引擎支持全文索引)。
全文索引類型為FULLTEXT,在定義索引的列上支持值的全文查找允許在這些索引列中插入重復(fù)值和空值。全文索引可以在Char、VarChar 上創(chuàng)建。
5.空間索引:
MySQL在5.7之后的版本支持了空間索引,而且支持OpenGIS幾何數(shù)據(jù)模型,MySQL在空間索引這方年遵循OpenGIS幾何數(shù)據(jù)模型規(guī)則。
6.前綴索引:
在文本類型為char、varchar、text類列上創(chuàng)建索引時(shí),可以指定索引列的長(zhǎng)度,但是數(shù)值類型不能指定。
6、如何判斷sql是否走索引?
通過(guò)explain關(guān)鍵字查看這條語(yǔ)句的執(zhí)行計(jì)劃
key:實(shí)際使用的索引,如果為NULL,則表示沒(méi)有使用索引
type:顯示查詢使用了何種類型,從好到壞,依次為:
system:表中只有一行數(shù)據(jù)
const:?jiǎn)伪碇凶疃嘀挥幸粭l匹配行,比如說(shuō)根據(jù)主鍵或者說(shuō)唯一索引進(jìn)行查詢
eq-ref:使用唯一索引,比如說(shuō)在聯(lián)表查詢中使用主鍵或者唯一索引作為關(guān)聯(lián)條件
ref:使用非唯一索引或者唯一索引前綴掃描
range:索引范圍掃描
index:全索引掃描
all:全表掃描
possible_key:能使用那個(gè)索引在表中找到該行
rows:大致估算出找到所需的記錄所需要讀取的行數(shù)
7、索引失效以及為什么失效
當(dāng)我們使用左或者左右模糊匹配的時(shí)候,也就是 like %xx 或者 like %xx%這兩種方式都會(huì)造成索引失效;因?yàn)樗饕峭ㄟ^(guò)前綴進(jìn)行構(gòu)建的。
當(dāng)我們?cè)诓樵儣l件中對(duì)索引列使用函數(shù),就會(huì)導(dǎo)致索引失效。因?yàn)镸ySQL沒(méi)有對(duì)使用了函數(shù)后的索引列創(chuàng)建索引。
當(dāng)我們?cè)诓樵儣l件中對(duì)索引列進(jìn)行表達(dá)式計(jì)算,也是無(wú)法走索引的。同上。
MySQL 在遇到字符串和數(shù)字比較的時(shí)候,會(huì)自動(dòng)把字符串轉(zhuǎn)為數(shù)字,然后再進(jìn)行比較。如果字符串是索引列,而條件語(yǔ)句中的輸入?yún)?shù)是數(shù)字的話,
那么索引列會(huì)發(fā)生隱式類型轉(zhuǎn)換,由于隱式類型轉(zhuǎn)換是通過(guò) CAST 函數(shù)實(shí)現(xiàn)的,等同于對(duì)索引列使用了函數(shù),所以就會(huì)導(dǎo)致索引失效。
聯(lián)合索引要能正確使用需要遵循最左匹配原則,也就是按照最左優(yōu)先的方式進(jìn)行索引的匹配,否則就會(huì)導(dǎo)致索引失效。
在 WHERE 子句中,如果在 OR 前的條件列是索引列,而在 OR 后的條件列不是索引列,那么索引會(huì)失效。
8、索引為什么能提高查詢性能
MySQL 的數(shù)據(jù)是持久化的,意味著數(shù)據(jù)(索引+記錄)是保存到磁盤上的,因?yàn)檫@樣即使設(shè)備斷電了,數(shù)據(jù)也不會(huì)丟失。
數(shù)據(jù)存儲(chǔ)在磁盤( SSD 跟 CPU 性能也不在一個(gè)量級(jí)),而磁盤處理數(shù)據(jù)很慢;提高磁盤性能主要通過(guò)減少 I/O 次數(shù),以及單次 I/O 有效數(shù)據(jù)量;
索引通過(guò)多階(一個(gè)節(jié)點(diǎn)保存多個(gè)數(shù)據(jù),指向多個(gè)子節(jié)點(diǎn))使樹(shù)的結(jié)構(gòu)更矮胖,從而減少 I/O 次數(shù);
索引通過(guò) B+ 樹(shù),把業(yè)務(wù)數(shù)據(jù)與索引數(shù)據(jù)分離,來(lái)提高單次 I/O 有效數(shù)據(jù)量,從而減少 I/O 次數(shù);
索引通過(guò)樹(shù)數(shù)據(jù)的有序和「二分查找」(多階樹(shù)可以假設(shè)為多分查找),大大縮小查詢范圍;
索引針對(duì)的是單個(gè)字段或部分字段,數(shù)據(jù)量本身比一條記錄的數(shù)據(jù)量要少的多,這樣即使通過(guò)掃描的方式查詢索引也比掃描數(shù)據(jù)庫(kù)表本身快的多