数据库索引详解
1、什么是索引?為什么要用索引?
1.1、索引的含義
數(shù)據(jù)庫(kù)索引,是數(shù)據(jù)庫(kù)管理系統(tǒng)中一個(gè)排序的數(shù)據(jù)結(jié)構(gòu),以協(xié)助快速查詢(xún),更新數(shù)據(jù)庫(kù)中表的數(shù)據(jù)。索引的實(shí)現(xiàn)通常使用B樹(shù)和變種的B+樹(shù)(MySQL常用的索引就是B+樹(shù))。除了數(shù)據(jù)之外,數(shù)據(jù)庫(kù)系統(tǒng)還維護(hù)為滿(mǎn)足特定查找算法的數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)以某種方式引用數(shù)據(jù),這種數(shù)據(jù)結(jié)構(gòu)就是索引。簡(jiǎn)言之,索引就類(lèi)似于書(shū)本,字典的目錄。
1.2、為什么用索引?
打個(gè)比方,如果正確合理設(shè)計(jì)使用索引的MySQL是一輛蘭博基尼的話(huà),那么沒(méi)有設(shè)計(jì)和使用索引的MySQL就是一個(gè)人力三輪車(chē)。對(duì)于沒(méi)有索引的表,單表查詢(xún)可能幾十萬(wàn)數(shù)據(jù)就是瓶頸,二通常大型網(wǎng)站單日就可能產(chǎn)生幾十萬(wàn)甚至幾百萬(wàn)的數(shù)據(jù),沒(méi)有索引查詢(xún)會(huì)變得非常緩慢。一言以蔽之,合理使用索引,可以加快數(shù)據(jù)庫(kù)的查詢(xún)效率和提升程序性能
索引的作用與缺點(diǎn)
2.1、作用
2.2、缺點(diǎn)
3、索引的使用場(chǎng)景
3.1、應(yīng)創(chuàng)建索引的場(chǎng)景
3.2、不應(yīng)該創(chuàng)建索引的場(chǎng)景
4、索引的分類(lèi)與說(shuō)明
4.1、主鍵索引
設(shè)定為主鍵后數(shù)據(jù)庫(kù)會(huì)自動(dòng)建立索引,innodb為聚簇索引
4.2、單列索引
一個(gè)索引只包含單個(gè)列,一個(gè)表可以有多個(gè)單列索引
4.3、唯一索引
索引列的值必須唯一,但允許有空值
4.4、復(fù)合索引
一個(gè)索引包含多個(gè)列,在數(shù)據(jù)庫(kù)操作期間,復(fù)合索引比單值索引所需要的開(kāi)銷(xiāo)更小(對(duì)于相同的多個(gè)列建索引)
如果一個(gè)表中的數(shù)據(jù)在查詢(xún)時(shí)有多個(gè)字段總是同時(shí)出現(xiàn)則這些字段就可以作為復(fù)合索引,形成索引覆蓋可以提高查詢(xún)的效率!
4.5、聚集索引與非聚集索引
4.5.1、聚集索引
聚集索引:指索引項(xiàng)的排序方式和表中數(shù)據(jù)記錄排序方式一致的索引。它會(huì)根據(jù)聚集索引鍵的順序來(lái)存儲(chǔ)表中的數(shù)據(jù),即對(duì)表的數(shù)據(jù)按索引鍵的順序進(jìn)行排序,然后重新存儲(chǔ)到磁盤(pán)上。因?yàn)閿?shù)據(jù)在物理存放只能有一種排列方式,所以一個(gè)表只能有一個(gè)聚集索引。比如字典中,用拼音查漢字,就是聚集索引。因?yàn)檎闹凶侄际前凑掌匆襞判虻?。而用偏旁部首查漢字,就是非聚集索引,因?yàn)檎闹械淖植⒉皇前凑掌圆渴着判虻?#xff0c;我們通過(guò)檢字表得到正文中的字在索引中的映射,然后通過(guò)映射找到所需要的字。
聚集索引的使用場(chǎng)合為:
查詢(xún)命令的回傳結(jié)果是以該字段為排序依據(jù)的;
查詢(xún)的結(jié)果返回一個(gè)區(qū)間的值;
查詢(xún)的結(jié)果返回某值相同的大量結(jié)果集
聚集索引會(huì)降低insert,和update操作的性能,所以,是否使用聚集索引要全面衡量。
4.5.2、非聚集索引
非聚集索引:與聚集索引相反,索引順序與物理存儲(chǔ)順序不一致
非聚集索引的使用場(chǎng)合為:
查詢(xún)所獲數(shù)據(jù)量較少時(shí);
某字段中的數(shù)據(jù)的唯一性比較高時(shí);
非聚集索引必須是稠密索引
4.5.3、使用及語(yǔ)法
create [unique] [clustered] [nonclustered] index index_name on {tabel/view} (column[dese/asc][....n])住:[ unique ] [clu stered] [nonclustered]表示要?jiǎng)?chuàng)建索引的類(lèi)型,以此為唯一索引,當(dāng)省略u(píng)nique選項(xiàng)時(shí),建立非唯一索引,當(dāng)省略clustered,nonclustered選項(xiàng)時(shí)。建立聚集索引,省略nonclusterrd選項(xiàng)時(shí),建立唯一聚集索引。
4.5.4、使用場(chǎng)景對(duì)比
| 列經(jīng)常被分組排序 | 使用 | 使用 |
| 返回某范圍內(nèi)的數(shù)據(jù) | 使用 | 不使用 |
| 一個(gè)或極少不同值 | 不使用 | 不使用 |
| 小數(shù)目的不同值 | 使用 | 不使用 |
| 大數(shù)目的不同值 | 不使用 | 使用 |
| 頻繁更新的列 | 不使用 | 使用 |
| 外鍵列 | 使用 | 使用 |
| 主鍵列 | 使用 | 使用 |
| 頻繁修改索引列 | 不使用 | 使用 |
4.6聚簇索引與非聚簇索引
4.6.1、聚簇索引
聚簇索引并不是一種單獨(dú)的索引類(lèi)型,而是一種數(shù)據(jù)存儲(chǔ)方式。將數(shù)據(jù)存儲(chǔ)于索引放到了一塊,索引結(jié)構(gòu)的葉子節(jié)點(diǎn)保存了行數(shù)據(jù)。
聚簇索引的特點(diǎn):
1、聚簇索引具有唯一性,由于聚簇索引是將數(shù)據(jù)跟索引結(jié)構(gòu)放到一塊,因此一個(gè)表僅有一個(gè)聚簇索引。
2、表中行的物理順序和索引中行的物理順序是相同的,在創(chuàng)建任何非聚簇索引之前創(chuàng)建聚簇索引,這是因?yàn)榫鄞厮饕淖兞诵械奈锢眄樞?#xff0c;數(shù)據(jù)行,按照一定的順序排列,并且自動(dòng)維護(hù)這個(gè)順序;
3、聚簇索引默認(rèn)是主鍵,如果表中沒(méi)有定義主鍵,InnoDB會(huì)選擇一個(gè)唯一且非空的索引代替。如果沒(méi)有這樣的索引,InnoDB會(huì)隱式定義一個(gè)主鍵(類(lèi)似oracle中的Rowld)來(lái)作為聚簇索引。如果已經(jīng)設(shè)置了主鍵為聚簇索引又希望再單獨(dú)設(shè)置聚簇索引,必須先刪除主鍵,然后再添加我們想要的聚簇索引,隨后恢復(fù)設(shè)置主鍵即可。
4.6.2、非聚簇索引
不是聚簇索引的二級(jí)索引,也叫作輔助索引,都稱(chēng)為非聚簇索引。將數(shù)據(jù)與索引分開(kāi)存儲(chǔ),索引結(jié)構(gòu)的葉子節(jié)點(diǎn)指向了數(shù)據(jù)對(duì)應(yīng)的位置。
4.6.3、MySQL的MyIsam和InnoDB存儲(chǔ)引擎
因?yàn)檫@兩種引擎對(duì)非聚簇索引和聚簇索引的使用,就是他們之間很大的一個(gè)區(qū)別。所以結(jié)合這兩個(gè)引擎,再對(duì)這兩種索引展開(kāi)些描述就更明了了。
在InnoDB中,在聚簇索引之上創(chuàng)建的索引稱(chēng)之為 索引,非聚簇索引都是輔助索引,像復(fù)合索引,前綴索引,唯一索引。輔助索引葉子節(jié)點(diǎn)存儲(chǔ)的不再是行的物理位置,而是主鍵值,輔助索引訪(fǎng)問(wèn)數(shù)據(jù)總是需要二次查找。
1、InnoDB使用的是聚簇索引,將主鍵組織到一棵B+樹(shù)中,而行數(shù)據(jù)就儲(chǔ)存在葉子節(jié)點(diǎn)上,若使用“where id = 14”這樣的條件查找主鍵,則按照B+樹(shù)的檢索算法即可查找到對(duì)應(yīng)的葉節(jié)點(diǎn),之后獲得行數(shù)據(jù)。
2、若對(duì)Name列進(jìn)行條件搜索,則需要兩個(gè)步驟:第一步在輔助索引B+樹(shù)中檢索Name,到達(dá)其葉子節(jié)點(diǎn)獲取對(duì)應(yīng)的主鍵。第二步使用主鍵在主索引B+樹(shù)中再執(zhí)行一次B+樹(shù)檢索操作,最終到達(dá)葉子節(jié)點(diǎn)即可獲取整行數(shù)據(jù)。(重點(diǎn)在于通過(guò)其他鍵需要建立輔助索引)
MyIsam使用的是非聚簇索引,非聚簇索引的兩顆B+樹(shù)看上去沒(méi)什么不同,節(jié)點(diǎn)的結(jié)構(gòu)完全一致只是存儲(chǔ)的內(nèi)容不同而已,主鍵索引B+樹(shù)的節(jié)點(diǎn)存儲(chǔ)了主鍵,輔助索引B+樹(shù)存儲(chǔ)了輔助鍵。表數(shù)據(jù)存儲(chǔ)在獨(dú)立的地方,這兩棵B+樹(shù)的葉子節(jié)點(diǎn)都使用一個(gè)地址指向真正的表數(shù)據(jù),對(duì)于表數(shù)據(jù)來(lái)說(shuō),這兩個(gè)鍵沒(méi)有任何差別。由于索引樹(shù)是獨(dú)立的,通過(guò)輔助鍵索引無(wú)需訪(fǎng)問(wèn)主鍵的索引樹(shù)。
4.6.4、對(duì)比總結(jié)
每次使用輔助索引檢索都要經(jīng)過(guò)兩次B+熟查找,看上去聚簇索引的效率明顯低于非聚簇索引,這不是多此一舉嗎?聚簇索引的優(yōu)勢(shì)在哪里?
1、由于行數(shù)據(jù)和聚簇索引的葉子節(jié)點(diǎn)存儲(chǔ)在一起,同一頁(yè)中會(huì)有多條行數(shù)據(jù),訪(fǎng)問(wèn)同一數(shù)據(jù)頁(yè)不同行記錄時(shí),已經(jīng)把頁(yè)加載到了Buffer中(緩存器),再次訪(fǎng)問(wèn)時(shí),會(huì)在內(nèi)存中完成訪(fǎng)問(wèn),不必訪(fǎng)問(wèn)磁盤(pán),這樣主鍵和行數(shù)據(jù)是一起被載入內(nèi)存的,找到葉子節(jié)點(diǎn)就可以立即將行數(shù)據(jù)返回了,如果按照主鍵ID來(lái)組織數(shù)據(jù),獲得數(shù)據(jù)更快。
2、輔助索引的葉子節(jié)點(diǎn),存儲(chǔ)主鍵值,而不是數(shù)據(jù)的存放地址。好處是當(dāng)行數(shù)據(jù)發(fā)生改變時(shí),索引樹(shù)的節(jié)點(diǎn)也需要分裂變化;或者是我們需要查找的數(shù)據(jù),在上一次IO讀寫(xiě)的緩存中沒(méi)有,需要發(fā)生一次新的IO操作時(shí),可以避免對(duì)輔助索引的維護(hù)工作,只需要維護(hù)聚簇索引樹(shù)就好了,另一個(gè)好處是,因?yàn)檩o助索引存放的是主鍵值,減少了輔助索引占用的存儲(chǔ)空間大小。
注:我們知道一次IO讀寫(xiě),可以獲取到16K大小的資源,我們稱(chēng)之為讀取到的數(shù)據(jù)區(qū)域?yàn)镻age。而我們的B樹(shù),B+樹(shù)的索引結(jié)構(gòu),葉子節(jié)點(diǎn)上存放好多個(gè)關(guān)鍵字(索引值)和對(duì)應(yīng)的數(shù)據(jù),都會(huì)在一次IO操作中被讀取到緩存中,所以在訪(fǎng)問(wèn)同一個(gè)頁(yè)中的不同記錄時(shí),會(huì)在內(nèi)存里操作,而不用再次進(jìn)行IO操作了。除非發(fā)生了頁(yè)的分裂,即要查詢(xún)的行數(shù)據(jù)不在上次IO操作的換襯里才會(huì)觸發(fā)新的IO操作。
3、因?yàn)镸yIsam的主索引并非聚簇索引,那么他的數(shù)據(jù)的物理地址必定是凌亂的,拿到這些物理地址,按照合適的算法進(jìn)行I/O讀取,于是開(kāi)始不停的尋道不停的旋轉(zhuǎn)。聚簇索引則只需一次I/O。(強(qiáng)烈的對(duì)比)
4、不過(guò),如果涉及到大數(shù)據(jù)的排序,全表掃描,count之類(lèi)的操作的話(huà),還是MyIsam占優(yōu)勢(shì)些,因?yàn)樗饕伎臻g小,這些操作是需要在內(nèi)存中完成的。
5、當(dāng)使用主鍵為聚簇索引時(shí),主鍵最好不要使用uuid,因?yàn)閡uid的值太過(guò)離散,不適合排序且可能出現(xiàn)新增加記錄的uuid,會(huì)插入在索引樹(shù)中間的位置,導(dǎo)致索引樹(shù)調(diào)整復(fù)雜度變大,消耗更多的時(shí)間和資源。建議使用int類(lèi)型的自增,方便排序并且默認(rèn)會(huì)在索引樹(shù)的末尾增加主鍵值,對(duì)索引樹(shù)的結(jié)構(gòu)影響最小。而且,主鍵值占用的存儲(chǔ)空間越大,輔助索引中保存的主鍵值也會(huì)跟著變大,占用存儲(chǔ)空間,也會(huì)影響到IO操作讀取到的數(shù)據(jù)量。
4.7、稠密索引與稀疏索引
在了解稠密索引和稀疏索引之前我們先來(lái)了解一下什么是聚焦索引。在一個(gè)文件中,可以有多個(gè)索引,分別基于不同的索引碼。如果包含數(shù)據(jù)記錄的文件按照某個(gè)指定的順序排列,那么該搜索碼對(duì)應(yīng)的索引就是聚焦索引。
4.7.1、稠密索引
在稠密索引中,文件中的每個(gè)搜索碼值都對(duì)應(yīng)一個(gè)索引值,也就是說(shuō),稠密索引為數(shù)據(jù)記錄文件的每一條記錄都設(shè)一個(gè)鍵-指針對(duì)。如下圖所示,索引項(xiàng)包括索引值以及指向搜索碼的第一條數(shù)據(jù)記錄的指針,即我們所說(shuō)的鍵-指針對(duì)。
4.7.2、稀疏索引
在稀疏索引中,只為搜索碼的某些值建立索引項(xiàng),也就是說(shuō),系數(shù)索引為數(shù)據(jù)記錄文件的每個(gè)存儲(chǔ)塊設(shè)一個(gè)鍵-指針對(duì),存儲(chǔ)塊意味著塊內(nèi)存存儲(chǔ)單元連續(xù),如下圖所示:
索引的底層原理
拋開(kāi)其他的數(shù)據(jù)庫(kù)索引實(shí)現(xiàn),主講MySQL的索引底層實(shí)現(xiàn),其底層是通過(guò)B+樹(shù)來(lái)實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)。
數(shù)據(jù)結(jié)構(gòu)存儲(chǔ),決定了數(shù)據(jù)查找和操作時(shí)的效率,包括時(shí)間復(fù)雜度和空間復(fù)雜度,而在取舍的時(shí)候,也無(wú)非就是時(shí)間換空間,空間換時(shí)間的權(quán)衡罷了,所以,這就很好的解釋了,為什么MySQL在索引的底層設(shè)計(jì)上,選用了B+樹(shù),而沒(méi)有選用B-樹(shù),或是紅黑樹(shù),AVL樹(shù)等等其他數(shù)據(jù)結(jié)構(gòu)??傊?#xff0c;就是使用B+樹(shù)作為索引的結(jié)構(gòu)存儲(chǔ),能在I/O性能上得到一個(gè)較大的優(yōu)勢(shì)。
那么具體優(yōu)勢(shì)在哪里呢?以B-樹(shù)與B+樹(shù)的對(duì)比,來(lái)闡述具體差異和B+樹(shù)的優(yōu)勢(shì)。
5.1、B-Tree
B-樹(shù)是一種多路自平衡的搜索樹(shù),它類(lèi)似普通的平衡二叉樹(shù),不同的一點(diǎn)是B-樹(shù)允許每個(gè)節(jié)點(diǎn)有更多的子節(jié)點(diǎn)。B-Tree相對(duì)于A(yíng)VLTree縮減了節(jié)點(diǎn)個(gè)數(shù),使每次磁盤(pán)I/O取到內(nèi)存的數(shù)據(jù)都發(fā)揮了作用,從而提高了查詢(xún)效率。
注:B-Tree就是我們常說(shuō)的B樹(shù)
那么m階B-Tree是滿(mǎn)足下列條件的數(shù)據(jù)結(jié)構(gòu):
所有鍵值分布在整棵樹(shù)中
搜索有可能在非葉子節(jié)點(diǎn)結(jié)束,在關(guān)鍵字全集內(nèi)做一次查找,性能逼近二分查找
每個(gè)節(jié)點(diǎn)最多擁有m個(gè)子樹(shù)
根節(jié)點(diǎn)至少有2個(gè)子樹(shù)
分支節(jié)點(diǎn)至少擁有m/2顆子樹(shù)(除根節(jié)點(diǎn)和葉子節(jié)點(diǎn)外都是分支節(jié)點(diǎn))
所有葉子節(jié)點(diǎn)都在同一層,每個(gè)節(jié)點(diǎn)最多可以有m-1個(gè)key,并且以升序排列
每個(gè)節(jié)點(diǎn)占用一個(gè)磁盤(pán)塊,一個(gè)節(jié)點(diǎn)上有兩個(gè)升序排序的關(guān)鍵字和三個(gè)指向子樹(shù)根節(jié)點(diǎn)的指針,指針存儲(chǔ)的是子節(jié)點(diǎn)所在磁盤(pán)塊的地址。兩個(gè)關(guān)鍵詞劃分成的三個(gè)范圍域?qū)?yīng)三個(gè)指針指向的子樹(shù)的數(shù)據(jù)的范圍域。以根節(jié)點(diǎn)為例,關(guān)鍵字為17和35,P1指針指向的子樹(shù)的數(shù)據(jù)范圍小于17,P2指針指向的子樹(shù)的數(shù)據(jù)范圍為17~35,P3指針指向的子樹(shù)的數(shù)據(jù)范圍大于35.
模擬查找關(guān)鍵字29的過(guò)程:
1、根據(jù)根節(jié)點(diǎn)找到磁盤(pán)塊1,讀入內(nèi)存?!敬疟P(pán)I/O操作第1次】
2、比較關(guān)鍵字29在區(qū)間(17,35),找到磁盤(pán)塊1的指針P2。
3、根據(jù)P2指針找到磁盤(pán)塊3,讀入內(nèi)存?!敬疟P(pán)I/O操作第2次】
4、比較關(guān)鍵字29在區(qū)間(26,30),找到磁盤(pán)塊3的指針P2。
5、根據(jù)P2指針找到磁盤(pán)塊8,讀入內(nèi)存。【磁盤(pán)I/O操作第3次】
6、在磁盤(pán)塊8中的關(guān)鍵字列表中找到關(guān)鍵字29。
7、分析上面過(guò)程,發(fā)現(xiàn)需要3次磁盤(pán)I/O操作,和3次內(nèi)存查找操作。由于內(nèi)存中的關(guān)鍵字是一個(gè)有序表結(jié)構(gòu),可以利用二分法查找提高效率。而3次磁盤(pán)I/O操作是影響整個(gè)B-Tree查找效率的決定因素。
但同時(shí)B-Tree也存在問(wèn)題:
每個(gè)節(jié)點(diǎn)中有key,也有data,而每一個(gè)頁(yè)的存儲(chǔ)空間是有限的,如果data數(shù)據(jù)較大時(shí)將會(huì)導(dǎo)致每個(gè)節(jié)點(diǎn)(即一個(gè)頁(yè))能存儲(chǔ)的key的數(shù)量很小。
當(dāng)存儲(chǔ)的數(shù)據(jù)量很大時(shí)同樣會(huì)導(dǎo)致B-Tree的深度較大,增大查詢(xún)時(shí)的磁盤(pán)I/O次數(shù),進(jìn)而影響查詢(xún)效率
5.2、B+Tree
B+Tree是在B-Tree基礎(chǔ)上的一種優(yōu)化,InnoDB存儲(chǔ)引擎就是用B+Tree實(shí)現(xiàn)其索引結(jié)構(gòu)。它帶來(lái)的變化點(diǎn):
B+樹(shù)每個(gè)節(jié)點(diǎn)可以包含更多的節(jié)點(diǎn),這樣做有兩個(gè)原因,一個(gè)是降低樹(shù)的高度。另外一個(gè)是將數(shù)據(jù)范圍變?yōu)槎鄠€(gè)區(qū)間,區(qū)間越多,數(shù)據(jù)檢索越快
非葉子節(jié)點(diǎn)存儲(chǔ)key,葉子節(jié)點(diǎn)存儲(chǔ)key和數(shù)據(jù)
葉子節(jié)點(diǎn)兩兩指針相互鏈接(符合磁盤(pán)的預(yù)讀特性),順序查詢(xún)性能更高
注:MySQL的InnoDB存儲(chǔ)引擎在設(shè)計(jì)時(shí)是將根節(jié)點(diǎn)常駐內(nèi)存,因此力求達(dá)到樹(shù)的深度不超過(guò)3,也就是說(shuō)I/O不需要超過(guò)3次。
通常在B+Tree上有兩個(gè)頭指針,一個(gè)指向根節(jié)點(diǎn),另一個(gè)指向關(guān)鍵字最小的葉子節(jié)點(diǎn),而且所有葉子節(jié)點(diǎn)(即數(shù)據(jù)節(jié)點(diǎn))之間是一種鏈?zhǔn)江h(huán)結(jié)構(gòu),因此可以對(duì)B+Tree進(jìn)行兩種查找運(yùn)算:一種是對(duì)于主鍵的范圍查找的分頁(yè)查找,另一種是從根節(jié)點(diǎn)開(kāi)始,進(jìn)行隨機(jī)查找。
5.3、B-樹(shù)和B+樹(shù)的區(qū)別
B+樹(shù)內(nèi)節(jié)點(diǎn)不存儲(chǔ)數(shù)據(jù),所有數(shù)據(jù)存儲(chǔ)在葉節(jié)點(diǎn)導(dǎo)致查詢(xún)時(shí)間復(fù)雜度固定為log n
B-樹(shù)查詢(xún)時(shí)間復(fù)雜度不固定,與Key在樹(shù)中的位置有關(guān),最好為O(1)
B+樹(shù)葉節(jié)點(diǎn)兩兩相連可大大增加區(qū)間訪(fǎng)問(wèn)性,可使用在范圍查詢(xún)等
B+樹(shù)更適合外部存儲(chǔ)(存儲(chǔ)磁盤(pán)數(shù)據(jù))。由于內(nèi)節(jié)點(diǎn)無(wú)data域,每個(gè)節(jié)點(diǎn)能索引的范圍更大更精確。
總結(jié)
- 上一篇: 多个小球碰撞的java_原生JS实现多个
- 下一篇: Java解析魔兽争霸3录像W3G文件(四