當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 小林coding
[導(dǎo)讀]索引是幫助MySQL高效獲取數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。更通俗的說(shuō),數(shù)據(jù)庫(kù)索引好比是一本書(shū)前面的目錄,能加快數(shù)據(jù)庫(kù)的查詢(xún)速度。

索引介紹

索引是什么

  • 官方介紹索引是幫助MySQL高效獲取數(shù)據(jù)數(shù)據(jù)結(jié)構(gòu)。更通俗的說(shuō),數(shù)據(jù)庫(kù)索引好比是一本書(shū)前面的目錄,能加快數(shù)據(jù)庫(kù)的查詢(xún)速度

  • 一般來(lái)說(shuō)索引本身也很大,不可能全部存儲(chǔ)在內(nèi)存中,因此索引往往是存儲(chǔ)在磁盤(pán)上的文件中的(可能存儲(chǔ)在單獨(dú)的索引文件中,也可能和數(shù)據(jù)一起存儲(chǔ)在數(shù)據(jù)文件中)。

  • 我們通常所說(shuō)的索引,包括聚集索引、覆蓋索引、組合索引、前綴索引、唯一索引等,沒(méi)有特別說(shuō)明,默認(rèn)都是使用B+樹(shù)結(jié)構(gòu)組織(多路搜索樹(shù),并不一定是二叉的)的索引。

索引的優(yōu)勢(shì)和劣勢(shì)

優(yōu)勢(shì):

  • 可以提高數(shù)據(jù)檢索的效率,降低數(shù)據(jù)庫(kù)的IO成本,類(lèi)似于書(shū)的目錄。

  • 通過(guò)索引列對(duì)數(shù)據(jù)進(jìn)行排序,降低數(shù)據(jù)排序的成本,降低了CPU的消耗。

    • 被索引的列會(huì)自動(dòng)進(jìn)行排序,包括【單列索引】和【組合索引】,只是組合索引的排序要復(fù)雜一些。
    • 如果按照索引列的順序進(jìn)行排序,對(duì)應(yīng)order by語(yǔ)句來(lái)說(shuō),效率就會(huì)提高很多。

劣勢(shì):

  • 索引會(huì)占據(jù)磁盤(pán)空間

  • 索引雖然會(huì)提高查詢(xún)效率,但是會(huì)降低更新表的效率。比如每次對(duì)表進(jìn)行增刪改操作,MySQL不僅要保存數(shù)據(jù),還有保存或者更新對(duì)應(yīng)的索引文件。

索引類(lèi)型

主鍵索引

索引列中的值必須是唯一的,不允許有空值。

普通索引

MySQL中基本索引類(lèi)型,沒(méi)有什么限制,允許在定義索引的列中插入重復(fù)值和空值。

唯一索引

索引列中的值必須是唯一的,但是允許為空值。

全文索引

只能在文本類(lèi)型CHAR,VARCHAR,TEXT類(lèi)型字段上創(chuàng)建全文索引。字段長(zhǎng)度比較大時(shí),如果創(chuàng)建普通索引,在進(jìn)行l(wèi)ike模糊查詢(xún)時(shí)效率比較低,這時(shí)可以創(chuàng)建全文索引。MyISAM和InnoDB中都可以使用全文索引。

空間索引

MySQL在5.7之后的版本支持了空間索引,而且支持OpenGIS幾何數(shù)據(jù)模型。MySQL在空間索引這方面遵循OpenGIS幾何數(shù)據(jù)模型規(guī)則。

前綴索引

在文本類(lèi)型如CHAR,VARCHAR,TEXT類(lèi)列上創(chuàng)建索引時(shí),可以指定索引列的長(zhǎng)度,但是數(shù)值類(lèi)型不能指定。

其他(按照索引列數(shù)量分類(lèi))

  1. 單列索引

  2. 組合索引

    組合索引的使用,需要遵循最左前綴匹配原則(最左匹配原則)。一般情況下在條件允許的情況下使用組合索引替代多個(gè)單列索引使用。

索引的數(shù)據(jù)結(jié)構(gòu)

Hash表

Hash表,在Java中的HashMap,TreeMap就是Hash表結(jié)構(gòu),以鍵值對(duì)的方式存儲(chǔ)數(shù)據(jù)。我們使用Hash表存儲(chǔ)表數(shù)據(jù)Key可以存儲(chǔ)索引列,Value可以存儲(chǔ)行記錄或者行磁盤(pán)地址。Hash表在等值查詢(xún)時(shí)效率很高,時(shí)間復(fù)雜度為O(1);但是不支持范圍快速查找,范圍查找時(shí)還是只能通過(guò)掃描全表方式。

顯然這種并不適合作為經(jīng)常需要查找和范圍查找的數(shù)據(jù)庫(kù)索引使用。

二叉查找樹(shù)

二叉樹(shù),我想大家都會(huì)在心里有個(gè)圖。

二叉樹(shù)特點(diǎn):每個(gè)節(jié)點(diǎn)最多有2個(gè)分叉,左子樹(shù)和右子樹(shù)數(shù)據(jù)順序左小右大。

這個(gè)特點(diǎn)就是為了保證每次查找都可以這折半而減少I(mǎi)O次數(shù),但是二叉樹(shù)就很考驗(yàn)第一個(gè)根節(jié)點(diǎn)的取值,因?yàn)楹苋菀自谶@個(gè)特點(diǎn)下出現(xiàn)我們并發(fā)想發(fā)生的情況“樹(shù)不分叉了”,這就很難受很不穩(wěn)定。

顯然這種情況不穩(wěn)定的我們?cè)龠x擇設(shè)計(jì)上必然會(huì)避免這種情況的

平衡二叉樹(shù)

平衡二叉樹(shù)是采用二分法思維,平衡二叉查找樹(shù)除了具備二叉樹(shù)的特點(diǎn),最主要的特征是樹(shù)的左右兩個(gè)子樹(shù)的層級(jí)最多相差1。在插入刪除數(shù)據(jù)時(shí)通過(guò)左旋/右旋操作保持二叉樹(shù)的平衡,不會(huì)出現(xiàn)左子樹(shù)很高、右子樹(shù)很矮的情況。

使用平衡二叉查找樹(shù)查詢(xún)的性能接近于二分查找法,時(shí)間復(fù)雜度是 O(log2n)。查詢(xún)id=6,只需要兩次IO。

就這個(gè)特點(diǎn)來(lái)看,可能各位會(huì)覺(jué)得這就很好,可以達(dá)到二叉樹(shù)的理想的情況了。然而依然存在一些問(wèn)題:

  1. 時(shí)間復(fù)雜度和樹(shù)高相關(guān)。樹(shù)有多高就需要檢索多少次,每個(gè)節(jié)點(diǎn)的讀取,都對(duì)應(yīng)一次磁盤(pán) IO 操作。樹(shù)的高度就等于每次查詢(xún)數(shù)據(jù)時(shí)磁盤(pán) IO 操作的次數(shù)。磁盤(pán)每次尋道時(shí)間為10ms,在表數(shù)據(jù)量大時(shí),查詢(xún)性能就會(huì)很差。(1百萬(wàn)的數(shù)據(jù)量,log2n約等于20次磁盤(pán)IO,時(shí)間20*10=0.2s)

  2. 平衡二叉樹(shù)不支持范圍查詢(xún)快速查找,范圍查詢(xún)時(shí)需要從根節(jié)點(diǎn)多次遍歷,查詢(xún)效率不高。

B樹(shù):改造二叉樹(shù)

MySQL的數(shù)據(jù)是存儲(chǔ)在磁盤(pán)文件中的,查詢(xún)處理數(shù)據(jù)時(shí),需要先把磁盤(pán)中的數(shù)據(jù)加載到內(nèi)存中,磁盤(pán)IO 操作非常耗時(shí),所以我們優(yōu)化的重點(diǎn)就是盡量減少磁盤(pán) IO 操作。訪問(wèn)二叉樹(shù)的每個(gè)節(jié)點(diǎn)就會(huì)發(fā)生一次IO,如果想要減少磁盤(pán)IO操作,就需要盡量降低樹(shù)的高度。那如何降低樹(shù)的高度呢?

假如key為bigint=8字節(jié),每個(gè)節(jié)點(diǎn)有兩個(gè)指針,每個(gè)指針為4個(gè)字節(jié),一個(gè)節(jié)點(diǎn)占用的空間16個(gè)字節(jié)(8+4*2=16)。

因?yàn)樵贛ySQL的InnoDB存儲(chǔ)引擎一次IO會(huì)讀取的一頁(yè)(默認(rèn)一頁(yè)16K)的數(shù)據(jù)量,而二叉樹(shù)一次IO有效數(shù)據(jù)量只有16字節(jié),空間利用率極低。為了最大化利用一次IO空間,一個(gè)簡(jiǎn)單的想法是在每個(gè)節(jié)點(diǎn)存儲(chǔ)多個(gè)元素,在每個(gè)節(jié)點(diǎn)盡可能多的存儲(chǔ)數(shù)據(jù)。每個(gè)節(jié)點(diǎn)可以存儲(chǔ)1000個(gè)索引(16k/16=1000),這樣就將二叉樹(shù)改造成了多叉樹(shù),通過(guò)增加樹(shù)的叉樹(shù),將樹(shù)從高瘦變?yōu)榘?。?gòu)建1百萬(wàn)條數(shù)據(jù),樹(shù)的高度只需要2層就可以(1000*1000=1百萬(wàn)),也就是說(shuō)只需要2次磁盤(pán)IO就可以查詢(xún)到數(shù)據(jù)。磁盤(pán)IO次數(shù)變少了,查詢(xún)數(shù)據(jù)的效率也就提高了。

這種數(shù)據(jù)結(jié)構(gòu)我們稱(chēng)為B樹(shù),B樹(shù)是一種多叉平衡查找樹(shù),如下圖主要特點(diǎn):

  1. B樹(shù)的節(jié)點(diǎn)中存儲(chǔ)著多個(gè)元素,每個(gè)內(nèi)節(jié)點(diǎn)有多個(gè)分叉。

  2. 節(jié)點(diǎn)中的元素包含鍵值和數(shù)據(jù),節(jié)點(diǎn)中的鍵值從大到小排列。也就是說(shuō),在所有的節(jié)點(diǎn)都儲(chǔ)存數(shù)據(jù)。

  3. 父節(jié)點(diǎn)當(dāng)中的元素不會(huì)出現(xiàn)在子節(jié)點(diǎn)中。

  4. 所有的葉子結(jié)點(diǎn)都位于同一層,葉節(jié)點(diǎn)具有相同的深度,葉節(jié)點(diǎn)之間沒(méi)有指針連接。

舉個(gè)例子,在b樹(shù)中查詢(xún)數(shù)據(jù)的情況:

假如我們查詢(xún)值等于10的數(shù)據(jù)。查詢(xún)路徑磁盤(pán)塊1->磁盤(pán)塊2->磁盤(pán)塊5。

第一次磁盤(pán)IO:將磁盤(pán)塊1加載到內(nèi)存中,在內(nèi)存中從頭遍歷比較,10<15,走左路,到磁盤(pán)尋址磁盤(pán)塊2。

第二次磁盤(pán)IO:將磁盤(pán)塊2加載到內(nèi)存中,在內(nèi)存中從頭遍歷比較,7<10,到磁盤(pán)中尋址定位到磁盤(pán)塊5。

第三次磁盤(pán)IO:將磁盤(pán)塊5加載到內(nèi)存中,在內(nèi)存中從頭遍歷比較,10=10,找到10,取出data,如果data存儲(chǔ)的行記錄,取出data,查詢(xún)結(jié)束。如果存儲(chǔ)的是磁盤(pán)地址,還需要根據(jù)磁盤(pán)地址到磁盤(pán)中取出數(shù)據(jù),查詢(xún)終止。

相比二叉平衡查找樹(shù),在整個(gè)查找過(guò)程中,雖然數(shù)據(jù)的比較次數(shù)并沒(méi)有明顯減少,但是磁盤(pán)IO次數(shù)會(huì)大大減少。同時(shí),由于我們的比較是在內(nèi)存中進(jìn)行的,比較的耗時(shí)可以忽略不計(jì)。B樹(shù)的高度一般2至3層就能滿(mǎn)足大部分的應(yīng)用場(chǎng)景,所以使用B樹(shù)構(gòu)建索引可以很好的提升查詢(xún)的效率。

過(guò)程如圖:

B樹(shù)索引查詢(xún)過(guò)程

看到這里一定覺(jué)得B樹(shù)就很理想了,但是前輩們會(huì)告訴你依然存在可以?xún)?yōu)化的地方:

  1. B樹(shù)不支持范圍查詢(xún)的快速查找,你想想這么一個(gè)情況如果我們想要查找10和35之間的數(shù)據(jù),查找到15之后,需要回到根節(jié)點(diǎn)重新遍歷查找,需要從根節(jié)點(diǎn)進(jìn)行多次遍歷,查詢(xún)效率有待提高。

  2. 如果data存儲(chǔ)的是行記錄,行的大小隨著列數(shù)的增多,所占空間會(huì)變大。這時(shí),一個(gè)頁(yè)中可存儲(chǔ)的數(shù)據(jù)量就會(huì)變少,樹(shù)相應(yīng)就會(huì)變高,磁盤(pán)IO次數(shù)就會(huì)變大。

B+樹(shù):改造B樹(shù)

B+樹(shù),作為B樹(shù)的升級(jí)版,在B樹(shù)基礎(chǔ)上,MySQL在B樹(shù)的基礎(chǔ)上繼續(xù)改造,使用B+樹(shù)構(gòu)建索引。B+樹(shù)和B樹(shù)最主要的區(qū)別在于非葉子節(jié)點(diǎn)是否存儲(chǔ)數(shù)據(jù)的問(wèn)題

  • B樹(shù):非葉子節(jié)點(diǎn)和葉子節(jié)點(diǎn)都會(huì)存儲(chǔ)數(shù)據(jù)。
  • B+樹(shù):只有葉子節(jié)點(diǎn)才會(huì)存儲(chǔ)數(shù)據(jù),非葉子節(jié)點(diǎn)至存儲(chǔ)鍵值。葉子節(jié)點(diǎn)之間使用雙向指針連接,最底層的葉子節(jié)點(diǎn)形成了一個(gè)雙向有序鏈表。
B+樹(shù)數(shù)據(jù)結(jié)構(gòu)

B+樹(shù)的最底層葉子節(jié)點(diǎn)包含了所有的索引項(xiàng)。從圖上可以看到,B+樹(shù)在查找數(shù)據(jù)的時(shí)候,由于數(shù)據(jù)都存放在最底層的葉子節(jié)點(diǎn)上,所以每次查找都需要檢索到葉子節(jié)點(diǎn)才能查詢(xún)到數(shù)據(jù)。

所以在需要查詢(xún)數(shù)據(jù)的情況下每次的磁盤(pán)的IO跟樹(shù)高有直接的關(guān)系,但是從另一方面來(lái)說(shuō),由于數(shù)據(jù)都被放到了葉子節(jié)點(diǎn),放索引的磁盤(pán)塊鎖存放的索引數(shù)量是會(huì)跟這增加的,相對(duì)于B樹(shù)來(lái)說(shuō),B+樹(shù)的樹(shù)高理論上情況下是比B樹(shù)要矮的。

也存在索引覆蓋查詢(xún)的情況,在索引中數(shù)據(jù)滿(mǎn)足了當(dāng)前查詢(xún)語(yǔ)句所需要的全部數(shù)據(jù),此時(shí)只需要找到索引即可立刻返回,不需要檢索到最底層的葉子節(jié)點(diǎn)。

舉個(gè)例子:等值查詢(xún)

假如我們查詢(xún)值等于9的數(shù)據(jù)。查詢(xún)路徑磁盤(pán)塊1->磁盤(pán)塊2->磁盤(pán)塊6。

第一次磁盤(pán)IO:將磁盤(pán)塊1加載到內(nèi)存中,在內(nèi)存中從頭遍歷比較,9<15,走左路,到磁盤(pán)尋址磁盤(pán)塊2。

第二次磁盤(pán)IO:將磁盤(pán)塊2加載到內(nèi)存中,在內(nèi)存中從頭遍歷比較,7<9<12,到磁盤(pán)中尋址定位到磁盤(pán)塊6。

第三次磁盤(pán)IO:將磁盤(pán)塊6加載到內(nèi)存中,在內(nèi)存中從頭遍歷比較,在第三個(gè)索引中找到9,取出data,如果data存儲(chǔ)的行記錄,取出data,查詢(xún)結(jié)束。如果存儲(chǔ)的是磁盤(pán)地址,還需要根據(jù)磁盤(pán)地址到磁盤(pán)中取出數(shù)據(jù),查詢(xún)終止。(這里需要區(qū)分的是在InnoDB中Data存儲(chǔ)的為行數(shù)據(jù),而MyIsam中存儲(chǔ)的是磁盤(pán)地址。)

過(guò)程如圖:

B+樹(shù)根據(jù)索引等值查詢(xún)過(guò)程

范圍查詢(xún):

假如我們想要查找9和26之間的數(shù)據(jù)。查找路徑是磁盤(pán)塊1->磁盤(pán)塊2->磁盤(pán)塊6->磁盤(pán)塊7。

首先查找值等于9的數(shù)據(jù),將值等于9的數(shù)據(jù)緩存到結(jié)果集。這一步和前面等值查詢(xún)流程一樣,發(fā)生了三次磁盤(pán)IO。

查找到15之后,底層的葉子節(jié)點(diǎn)是一個(gè)有序列表,我們從磁盤(pán)塊6,鍵值9開(kāi)始向后遍歷篩選所有符合篩選條件的數(shù)據(jù)。

第四次磁盤(pán)IO:根據(jù)磁盤(pán)6后繼指針到磁盤(pán)中尋址定位到磁盤(pán)塊7,將磁盤(pán)7加載到內(nèi)存中,在內(nèi)存中從頭遍歷比較,9<25<26,9<26<=26,將data緩存到結(jié)果集。

主鍵具備唯一性(后面不會(huì)有<=26的數(shù)據(jù)),不需再向后查找,查詢(xún)終止。將結(jié)果集返回給用戶(hù)。

可以看到B+樹(shù)可以保證等值和范圍查詢(xún)的快速查找,MySQL的索引就采用了B+樹(shù)的數(shù)據(jù)結(jié)構(gòu)。

Mysql的索引實(shí)現(xiàn)

介紹完了索引數(shù)據(jù)結(jié)構(gòu),那肯定是要帶入到Mysql里面看看真實(shí)的使用場(chǎng)景的,所以這里分析Mysql的兩種存儲(chǔ)引擎的索引實(shí)現(xiàn):MyISAM索引InnoDB索引

MyIsam索引

以一個(gè)簡(jiǎn)單的user表為例。user表存在兩個(gè)索引,id列為主鍵索引,age列為普通索引

CREATE?TABLE?`user`
(
??`id`???????int(11)?NOT?NULL?AUTO_INCREMENT,
??`username`?varchar(20)?DEFAULT?NULL,
??`age`??????int(11)?????DEFAULT?NULL,
??PRIMARY?KEY?(`id`)?USING?BTREE,
??KEY?`idx_age`?(`age`)?USING?BTREE
)?ENGINE?=?MyISAM
??AUTO_INCREMENT?=?1
??DEFAULT?CHARSET?=?utf8;
MyIsam_user查詢(xún)數(shù)據(jù)

MyISAM的數(shù)據(jù)文件和索引文件是分開(kāi)存儲(chǔ)的。MyISAM使用B+樹(shù)構(gòu)建索引樹(shù)時(shí),葉子節(jié)點(diǎn)中存儲(chǔ)的鍵值為索引列的值,數(shù)據(jù)為索引所在行的磁盤(pán)地址。

主鍵索引

MyIsam主鍵索引

表user的索引存儲(chǔ)在索引文件user.MYI中,數(shù)據(jù)文件存儲(chǔ)在數(shù)據(jù)文件 user.MYD中。

簡(jiǎn)單分析下查詢(xún)時(shí)的磁盤(pán)IO情況:

根據(jù)主鍵等值查詢(xún)數(shù)據(jù):

select * from user where id = 28;
  1. 先在主鍵樹(shù)中從根節(jié)點(diǎn)開(kāi)始檢索,將根節(jié)點(diǎn)加載到內(nèi)存,比較28<75,走左路。(1次磁盤(pán)IO)
  2. 將左子樹(shù)節(jié)點(diǎn)加載到內(nèi)存中,比較16<28<47,向下檢索。(1次磁盤(pán)IO)
  3. 檢索到葉節(jié)點(diǎn),將節(jié)點(diǎn)加載到內(nèi)存中遍歷,比較16<28,18<28,28=28。查找到值等于30的索引項(xiàng)。(1次磁盤(pán)IO)
  4. 從索引項(xiàng)中獲取磁盤(pán)地址,然后到數(shù)據(jù)文件user.MYD中獲取對(duì)應(yīng)整行記錄。(1次磁盤(pán)IO)
  5. 將記錄返給客戶(hù)端。

磁盤(pán)IO次數(shù):3次索引檢索+記錄數(shù)據(jù)檢索。

根據(jù)主鍵范圍查詢(xún)數(shù)據(jù):

select?*?from?user?where?id?between?28?and?47;
  1. 先在主鍵樹(shù)中從根節(jié)點(diǎn)開(kāi)始檢索,將根節(jié)點(diǎn)加載到內(nèi)存,比較28<75,走左路。(1次磁盤(pán)IO)

  2. 將左子樹(shù)節(jié)點(diǎn)加載到內(nèi)存中,比較16<28<47,向下檢索。(1次磁盤(pán)IO)

  3. 檢索到葉節(jié)點(diǎn),將節(jié)點(diǎn)加載到內(nèi)存中遍歷比較16<28,18<28,28=28<47。查找到值等于28的索引項(xiàng)。

    根據(jù)磁盤(pán)地址從數(shù)據(jù)文件中獲取行記錄緩存到結(jié)果集中。(1次磁盤(pán)IO)

    我們的查詢(xún)語(yǔ)句時(shí)范圍查找,需要向后遍歷底層葉子鏈表,直至到達(dá)最后一個(gè)不滿(mǎn)足篩選條件。

  4. 向后遍歷底層葉子鏈表,將下一個(gè)節(jié)點(diǎn)加載到內(nèi)存中,遍歷比較,28<47=47,根據(jù)磁盤(pán)地址從數(shù)據(jù)文件中獲取行記錄緩存到結(jié)果集中。(1次磁盤(pán)IO)

  5. 最后得到兩條符合篩選條件,將查詢(xún)結(jié)果集返給客戶(hù)端。

磁盤(pán)IO次數(shù):4次索引檢索+記錄數(shù)據(jù)檢索。

MyIsam索引范圍查詢(xún)過(guò)程

備注:以上分析僅供參考,MyISAM在查詢(xún)時(shí),會(huì)將索引節(jié)點(diǎn)緩存在MySQL緩存中,而數(shù)據(jù)緩存依賴(lài)于操作系統(tǒng)自身的緩存,所以并不是每次都是走的磁盤(pán),這里只是為了分析索引的使用過(guò)程。

輔助索引

在 MyISAM 中,輔助索引和主鍵索引的結(jié)構(gòu)是一樣的,沒(méi)有任何區(qū)別,葉子節(jié)點(diǎn)的數(shù)據(jù)存儲(chǔ)的都是行記錄的磁盤(pán)地址。只是主鍵索引的鍵值是唯一的,而輔助索引的鍵值可以重復(fù)。

查詢(xún)數(shù)據(jù)時(shí),由于輔助索引的鍵值不唯一,可能存在多個(gè)擁有相同的記錄,所以即使是等值查詢(xún),也需要按照范圍查詢(xún)的方式在輔助索引樹(shù)中檢索數(shù)據(jù)。

InnoDB索引

主鍵索引(聚簇索引)

每個(gè)InnoDB表都有一個(gè)聚簇索引 ,聚簇索引使用B+樹(shù)構(gòu)建,葉子節(jié)點(diǎn)存儲(chǔ)的數(shù)據(jù)是整行記錄。一般情況下,聚簇索引等同于主鍵索引,當(dāng)一個(gè)表沒(méi)有創(chuàng)建主鍵索引時(shí),InnoDB會(huì)自動(dòng)創(chuàng)建一個(gè)ROWID字段來(lái)構(gòu)建聚簇索引。InnoDB創(chuàng)建索引的具體規(guī)則如下:

  1. 在表上定義主鍵PRIMARY KEY,InnoDB將主鍵索引用作聚簇索引。
  2. 如果表沒(méi)有定義主鍵,InnoDB會(huì)選擇第一個(gè)不為NULL的唯一索引列用作聚簇索引。
  3. 如果以上兩個(gè)都沒(méi)有,InnoDB 會(huì)使用一個(gè)6 字節(jié)長(zhǎng)整型的隱式字段 ROWID字段構(gòu)建聚簇索引。該ROWID字段會(huì)在插入新行時(shí)自動(dòng)遞增。

除聚簇索引之外的所有索引都稱(chēng)為輔助索引。在中InnoDB,輔助索引中的葉子節(jié)點(diǎn)存儲(chǔ)的數(shù)據(jù)是該行的主鍵值都。在檢索時(shí),InnoDB使用此主鍵值在聚簇索引中搜索行記錄。

這里以u(píng)ser_innodb為例,user_innodb的id列為主鍵,age列為普通索引。

CREATE?TABLE?`user_innodb`
(
??`id`???????int(11)?NOT?NULL?AUTO_INCREMENT,
??`username`?varchar(20)?DEFAULT?NULL,
??`age`??????int(11)?????DEFAULT?NULL,
??PRIMARY?KEY?(`id`)?USING?BTREE,
??KEY?`idx_age`?(`age`)?USING?BTREE
)?ENGINE?=?InnoDB;
user數(shù)據(jù)

InnoDB的數(shù)據(jù)和索引存儲(chǔ)在一個(gè)文件t_user_innodb.ibd中。InnoDB的數(shù)據(jù)組織方式,是聚簇索引。

主鍵索引的葉子節(jié)點(diǎn)會(huì)存儲(chǔ)數(shù)據(jù)行,輔助索引只會(huì)存儲(chǔ)主鍵值。

InnoDB主鍵索引

等值查詢(xún)數(shù)據(jù):

select?*?from?user_innodb?where?id?=?28;
  1. 先在主鍵樹(shù)中從根節(jié)點(diǎn)開(kāi)始檢索,將根節(jié)點(diǎn)加載到內(nèi)存,比較28<75,走左路。(1次磁盤(pán)IO)

    將左子樹(shù)節(jié)點(diǎn)加載到內(nèi)存中,比較16<28<47,向下檢索。(1次磁盤(pán)IO)

    檢索到葉節(jié)點(diǎn),將節(jié)點(diǎn)加載到內(nèi)存中遍歷,比較16<28,18<28,28=28。查找到值等于28的索引項(xiàng),直接可以獲取整行數(shù)據(jù)。將改記錄返回給客戶(hù)端。(1次磁盤(pán)IO)

    磁盤(pán)IO數(shù)量:3次。


輔助索引

除聚簇索引之外的所有索引都稱(chēng)為輔助索引,InnoDB的輔助索引只會(huì)存儲(chǔ)主鍵值而非磁盤(pán)地址。

以表user_innodb的age列為例,age索引的索引結(jié)果如下圖。

InnoDB輔助索引

底層葉子節(jié)點(diǎn)的按照(age,id)的順序排序,先按照age列從小到大排序,age列相同時(shí)按照id列從小到大排序。

使用輔助索引需要檢索兩遍索引:首先檢索輔助索引獲得主鍵,然后使用主鍵到主索引中檢索獲得記錄。

畫(huà)圖分析等值查詢(xún)的情況:

select?*?from?t_user_innodb?where?age=19;
InnoDB輔助索引查詢(xún)

根據(jù)在輔助索引樹(shù)中獲取的主鍵id,到主鍵索引樹(shù)檢索數(shù)據(jù)的過(guò)程稱(chēng)為回表查詢(xún)。

磁盤(pán)IO數(shù):輔助索引3次+獲取記錄回表3次

組合索引

還是以自己創(chuàng)建的一個(gè)表為例:表 abc_innodb,id為主鍵索引,創(chuàng)建了一個(gè)聯(lián)合索引idx_abc(a,b,c)。

CREATE?TABLE?`abc_innodb`
(
??`id`?int(11)?NOT?NULL?AUTO_INCREMENT,
??`a`??int(11)?????DEFAULT?NULL,
??`b`??int(11)?????DEFAULT?NULL,
??`c`??varchar(10)?DEFAULT?NULL,
??`d`??varchar(10)?DEFAULT?NULL,
??PRIMARY?KEY?(`id`)?USING?BTREE,
??KEY?`idx_abc`?(`a`,?`b`,?`c`)
)?ENGINE?=?InnoDB;

select * from abc_innodb order by a, b, c, id;

組合索引的數(shù)據(jù)結(jié)構(gòu):

組合索引結(jié)構(gòu)1

組合索引的查詢(xún)過(guò)程:

select?*?from?abc_innodb?where?a?=?13?and?b?=?16?and?c?=?4;
組合索引的查詢(xún)過(guò)程

最左匹配原則:

最左前綴匹配原則和聯(lián)合索引的索引存儲(chǔ)結(jié)構(gòu)和檢索方式是有關(guān)系的。

在組合索引樹(shù)中,最底層的葉子節(jié)點(diǎn)按照第一列a列從左到右遞增排列,但是b列和c列是無(wú)序的,b列只有在a列值相等的情況下小范圍內(nèi)遞增有序,而c列只能在a,b兩列相等的情況下小范圍內(nèi)遞增有序。

就像上面的查詢(xún),B+樹(shù)會(huì)先比較a列來(lái)確定下一步應(yīng)該搜索的方向,往左還是往右。如果a列相同再比較b列。但是如果查詢(xún)條件沒(méi)有a列,B+樹(shù)就不知道第一步應(yīng)該從哪個(gè)節(jié)點(diǎn)查起。

可以說(shuō)創(chuàng)建的idx_abc(a,b,c)索引,相當(dāng)于創(chuàng)建了(a)、(a,b)(a,b,c)三個(gè)索引。、

組合索引的最左前綴匹配原則:使用組合索引查詢(xún)時(shí),mysql會(huì)一直向右匹配直至遇到范圍查詢(xún)(>、<、between、like)就停止匹配。

覆蓋索引

覆蓋索引并不是說(shuō)是索引結(jié)構(gòu),覆蓋索引是一種很常用的優(yōu)化手段。因?yàn)樵谑褂幂o助索引的時(shí)候,我們只可以拿到主鍵值,相當(dāng)于獲取數(shù)據(jù)還需要再根據(jù)主鍵查詢(xún)主鍵索引再獲取到數(shù)據(jù)。但是試想下這么一種情況,在上面abc_innodb表中的組合索引查詢(xún)時(shí),如果我只需要abc字段的,那是不是意味著我們查詢(xún)到組合索引的葉子節(jié)點(diǎn)就可以直接返回了,而不需要回表。這種情況就是覆蓋索引

可以看一下執(zhí)行計(jì)劃:

覆蓋索引的情況:

使用到覆蓋索引

未使用到覆蓋索引:

總結(jié)

看到這里,你是不是對(duì)于自己的sql語(yǔ)句里面的索引的有了更多優(yōu)化想法呢。

比如:

避免回表

在InnoDB的存儲(chǔ)引擎中,使用輔助索引查詢(xún)的時(shí)候,因?yàn)檩o助索引葉子節(jié)點(diǎn)保存的數(shù)據(jù)不是當(dāng)前記錄的數(shù)據(jù)而是當(dāng)前記錄的主鍵索引,索引如果需要獲取當(dāng)前記錄完整數(shù)據(jù)就必然需要根據(jù)主鍵值從主鍵索引繼續(xù)查詢(xún)。這個(gè)過(guò)程我們成位回表。想想回表必然是會(huì)消耗性能影響性能。那如何避免呢?

使用索引覆蓋,舉個(gè)例子:現(xiàn)有User表(id(PK),name(key),sex,address,hobby...)

如果在一個(gè)場(chǎng)景下,select id,name,sex from user where name ='zhangsan';這個(gè)語(yǔ)句在業(yè)務(wù)上頻繁使用到,而user表的其他字段使用頻率遠(yuǎn)低于它,在這種情況下,如果我們?cè)诮?name 字段的索引的時(shí)候,不是使用單一索引,而是使用聯(lián)合索引(name,sex)這樣的話再執(zhí)行這個(gè)查詢(xún)語(yǔ)句是不是根據(jù)輔助索引查詢(xún)到的結(jié)果就可以獲取當(dāng)前語(yǔ)句的完整數(shù)據(jù)。

這樣就可以有效地避免了回表再獲取sex的數(shù)據(jù)。

這里就是一個(gè)典型的使用覆蓋索引的優(yōu)化策略減少回表的情況。

聯(lián)合索引的使用

聯(lián)合索引,在建立索引的時(shí)候,盡量在多個(gè)單列索引上判斷下是否可以使用聯(lián)合索引。聯(lián)合索引的使用不僅可以節(jié)省空間,還可以更容易的使用到索引覆蓋。

試想一下,索引的字段越多,是不是更容易滿(mǎn)足查詢(xún)需要返回的數(shù)據(jù)呢。比如聯(lián)合索引(a_b_c),是不是等于有了索引:a,a_b,a_b_c三個(gè)索引,這樣是不是節(jié)省了空間,當(dāng)然節(jié)省的空間并不是三倍于(a,a_b,a_b_c)三個(gè)索引,因?yàn)樗饕龢?shù)的數(shù)據(jù)沒(méi)變,但是索引data字段的數(shù)據(jù)確實(shí)真實(shí)的節(jié)省了。

聯(lián)合索引的創(chuàng)建原則,在創(chuàng)建聯(lián)合索引的時(shí)候因該把頻繁使用的列、區(qū)分度高的列放在前面,頻繁使用代表索引利用率高,區(qū)分度高代表篩選粒度大,這些都是在索引創(chuàng)建的需要考慮到的優(yōu)化場(chǎng)景,也可以在常需要作為查詢(xún)返回的字段上增加到聯(lián)合索引中,如果在聯(lián)合索引上增加一個(gè)字段而使用到了覆蓋索引,那我建議這種情況下使用聯(lián)合索引。

聯(lián)合索引的使用

  1. 考慮當(dāng)前是否已經(jīng)存在多個(gè)可以合并的單列索引,如果有,那么將當(dāng)前多個(gè)單列索引創(chuàng)建為一個(gè)聯(lián)合索引。
  2. 當(dāng)前索引存在頻繁使用作為返回字段的列,這個(gè)時(shí)候就可以考慮當(dāng)前列是否可以加入到當(dāng)前已經(jīng)存在索引上,使其查詢(xún)語(yǔ)句可以使用到覆蓋索引。

好啦以上就是我自己對(duì)索引的一些小總結(jié),有點(diǎn)小長(zhǎng)有點(diǎn)枯燥,適合收藏后慢慢看。


哈嘍,我是小林,時(shí)而圖解技術(shù),時(shí)而說(shuō)些雜事,時(shí)而拍拍貓片。


推薦閱讀

索引為什么能提高查詢(xún)性能....

一口氣搞懂「文件系統(tǒng)」,就靠這 25 張圖了

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車(chē)的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車(chē)技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車(chē)工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車(chē)。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車(chē) 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶(hù)希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱(chēng),數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱(chēng)"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉