新聞中心
在工作中我們常常用到索引,無(wú)論是普通索引還是唯一索引,都是一些常用的索引方式,目的就是為了提高查詢(xún)效率,避免業(yè)務(wù)請(qǐng)求超時(shí)等問(wèn)題。那么,當(dāng)你在使用索引的時(shí)候,是否思考過(guò)以下問(wèn)題:索引是如何實(shí)現(xiàn)的,以及一共有哪些索引呢?

帶著這些問(wèn)題,讓我們開(kāi)始吧~
什么是索引
不知道你第一次聽(tīng)到索引二字,是否會(huì)覺(jué)得它非常抽象,且難以理解。索引,什么是索引呢?
索引其實(shí)就是一種數(shù)據(jù)結(jié)構(gòu)。那么在計(jì)算機(jī)世界中,任何數(shù)據(jù)結(jié)構(gòu)都有它的用途,如棧、堆、樹(shù)、隊(duì)列等。那么,數(shù)據(jù)庫(kù)又什么要使用索引?為了提高查詢(xún)(檢索)效率的啦。所以,它就如同書(shū)本最前面的目錄一樣,作用就是作為檢索和排序,來(lái)極大程度提升你對(duì)數(shù)據(jù)的查詢(xún)速度。
所以,你可以理解索引是幫助存儲(chǔ)引擎高效獲取數(shù)據(jù)的一種方式,就如同你為了高效獲取書(shū)中的某一塊知識(shí)而去看書(shū)本目錄一樣。
索引的實(shí)現(xiàn)方式
索引通常使用的是叫做B樹(shù)(B Tree)和B+樹(shù)(B+ Tree)的數(shù)據(jù)結(jié)構(gòu)。也會(huì)使用哈希(Hash)作為索引,這幾種方式我們都會(huì)聊聊。
B樹(shù)
B Tree 指的就是 Balance Tree,也就是平衡樹(shù)。說(shuō)說(shuō)它的特點(diǎn):
- 它是一顆多路查找樹(shù),并且所有葉子節(jié)點(diǎn)位于同一層
- 每個(gè)節(jié)點(diǎn)中不僅包含數(shù)據(jù)的鍵值,還有data值
- 每個(gè)節(jié)點(diǎn)都相當(dāng)于一個(gè)磁盤(pán)塊
你可以對(duì)它進(jìn)行存儲(chǔ)數(shù)據(jù)、對(duì)其進(jìn)行排序,由于是順序進(jìn)行讀取、插入和刪除,所以它的時(shí)間復(fù)雜度為O(log n)。
B+樹(shù)
B+ Tree是基于B Tree實(shí)現(xiàn)的。它具有B Tree的平衡性,并且由于是有序的,也可以通過(guò)指針來(lái)進(jìn)行順序訪問(wèn),時(shí)間復(fù)雜度依然為O(log n):
說(shuō)說(shuō)它的特點(diǎn):
- 每個(gè)葉子節(jié)存儲(chǔ)索引字段的鍵值及字段對(duì)應(yīng)的數(shù)據(jù)
- 非葉子節(jié)點(diǎn)只存儲(chǔ)索引字段的鍵值及指向子節(jié)點(diǎn)的指針,不存儲(chǔ)數(shù)據(jù)
- 每個(gè)結(jié)點(diǎn)相當(dāng)于一個(gè)磁盤(pán)塊
- 同一層級(jí)的葉子節(jié)點(diǎn)之間以雙向鏈表的形式相連
那么,為什么有了B樹(shù),還需要B+樹(shù)呢?
其實(shí),B樹(shù)和B+樹(shù)的最大區(qū)別就在于:B+樹(shù)的非葉子節(jié)點(diǎn)只會(huì)存儲(chǔ)指向下一個(gè)節(jié)點(diǎn)的地址,它并不存儲(chǔ)實(shí)際的值。并且它所有的葉子節(jié)點(diǎn)之間都是使用鏈表進(jìn)行連接,由于是有序的,這樣進(jìn)行遍歷查找會(huì)更快。
所以,B+樹(shù)就具備了B樹(shù)不具備的優(yōu)點(diǎn)。由于非葉子節(jié)點(diǎn)不存儲(chǔ)數(shù)據(jù),在同樣空間的內(nèi)存頁(yè)當(dāng)中,就可以存放更多的key,這些key緊密、順序地排列,對(duì)于數(shù)據(jù)的查詢(xún)來(lái)說(shuō)會(huì)比B樹(shù)更加穩(wěn)定和迅速,因?yàn)樗軌蚋玫乩每臻g局部性原理。
Hash
除了B樹(shù)和B+樹(shù),還有一種索引就是哈希了。哈希索引就是基于哈希算法,對(duì)于每一行數(shù)據(jù),數(shù)據(jù)庫(kù)存儲(chǔ)引擎會(huì)對(duì)所有索引列都通過(guò)哈希算法去計(jì)算一個(gè)哈希碼,然后將這個(gè)哈希碼存儲(chǔ)在哈希索引中,由于使用的是哈希算法,所以使用哈希索引就會(huì)存在兩個(gè)弊端:
哈希算法計(jì)算出來(lái)的哈希值可能存在哈希沖突
由于計(jì)算出來(lái)的是個(gè)值所以無(wú)法進(jìn)行范圍查詢(xún)
所以,B樹(shù)和B+樹(shù)是數(shù)據(jù)庫(kù)中比較常用到的兩種索引數(shù)據(jù)結(jié)構(gòu),而對(duì)于MySQL InnoDB存儲(chǔ)引擎來(lái)說(shuō),它使用的是B+樹(shù)。
索引的分類(lèi)
我們剛剛講解的是索引的實(shí)現(xiàn)方式,那么對(duì)于索引還能將它以實(shí)際的應(yīng)用功能劃分和按照物理實(shí)現(xiàn)來(lái)進(jìn)行劃分。
按照功能劃分
我們平時(shí)在寫(xiě)SQL或者建表的時(shí)候,會(huì)根據(jù)實(shí)際的業(yè)務(wù)查詢(xún)頻率、特點(diǎn)和數(shù)據(jù)量為字段建立各種各樣的索引。那么這些索引可以根據(jù)實(shí)際應(yīng)用來(lái)進(jìn)行分類(lèi):
- 主鍵(PRIMARY KEY)
- 唯一索引(UNIQUE)
- 普通索引(INDEX)
- 全文索引(FULLTEXT)
主鍵
在建表的時(shí)候只要指定了主鍵,就會(huì)生成對(duì)應(yīng)的索引。
為什么我這里說(shuō)主鍵,而不說(shuō)主鍵索引呢。很多人將主鍵和唯一索引、普通索引這些掛鉤在一起,稱(chēng)之為“主鍵索引”。其實(shí)這是不準(zhǔn)確的。因?yàn)橹麈I本質(zhì)上來(lái)說(shuō),它是一種約束,而索引是一種數(shù)據(jù)結(jié)構(gòu),用來(lái)提升查詢(xún)效率。兩者不是一個(gè)層面的東西,也有著功能上質(zhì)的區(qū)別。
在這里我將主鍵根據(jù)功能劃分將它放進(jìn)來(lái),因?yàn)樗沁@些索引 、甚至是一張表的基礎(chǔ),對(duì)于MySQL的InnoDB存儲(chǔ)引擎來(lái)說(shuō),一個(gè)表結(jié)構(gòu)一定要有一個(gè)主鍵,表中的數(shù)據(jù)都是按照主鍵順序進(jìn)行存放的。其它索引,也都要依賴(lài)于主鍵來(lái)生成索引。
主鍵的創(chuàng)建方式:
方式1:創(chuàng)建表的時(shí)候指定主鍵,如果創(chuàng)建表時(shí)沒(méi)有顯示定義主鍵,InnoDB就會(huì)通過(guò)以下規(guī)則去創(chuàng)建一個(gè)主鍵:
判斷表中是否存在一個(gè)非空的唯一索引。如果存在,則該索引對(duì)應(yīng)的字段名即為主鍵。如果不存在,則InnoDB會(huì)自動(dòng)創(chuàng)建索引
方式2:通過(guò)SQL語(yǔ)句:
ALTER TABLE `table_name` ADD PRIMARY KEY `column_name`;
那們,我們常常說(shuō)的“主鍵索引”又是怎么一回事呢?一會(huì)我們就會(huì)接觸到。
唯一索引
唯一索引就是作為一種索引,可以確保這行索引列不包含重復(fù)的值。所以它和非唯一索引的區(qū)別就是除了具有索引的功能,還限制這一字段在數(shù)據(jù)庫(kù)中不能有重復(fù)記錄,我們可以通過(guò)以下SQL創(chuàng)建唯一索引:
ALTER TABLE `table _name` ADD UNIQUE indexName;
普通索引
樸實(shí)無(wú)華,最普通的索引類(lèi)型了。將一個(gè)或者多個(gè)字段添加索引,僅僅用來(lái)增加查詢(xún)速度。fancy在工作中創(chuàng)建最多的就是普通索引了。它的創(chuàng)建語(yǔ)句:
ALTER TABLE `table_name` ADD INDEX index_name;
全文索引
作為程序員,我們每日都要和搜索引擎打交道了。不僅僅是搜索引擎,在很多app上,都有一個(gè)全文搜索的功能:
如果不去使用一些中間件技術(shù),我們的每一次搜索,都要去根據(jù)關(guān)鍵詞去數(shù)據(jù)庫(kù)中查找。
如果不使用索引,那么在海量的數(shù)據(jù)里面要將你預(yù)期的結(jié)果篩選出來(lái),可能要花費(fèi)好幾秒的響應(yīng)時(shí)間。這對(duì)于用戶(hù)來(lái)說(shuō)無(wú)疑是不可接受的。
所以,全文索引,就適合應(yīng)用于這樣的業(yè)務(wù)場(chǎng)景。
如果想要使用全文索引,就必須指定存儲(chǔ)引擎為MYISAM,因?yàn)镮nnoDB存儲(chǔ)引擎是不支持全文索引的。
由于全文索引存在許多弊端,比如不支持大小寫(xiě)、索引創(chuàng)建慢等問(wèn)題,所以不建議使用MySQL的全文索引。在現(xiàn)在的場(chǎng)景中,我們一般使用ElasticSearch這種封裝好的中間件來(lái)滿(mǎn)足全局?jǐn)?shù)據(jù)檢索的需求。全文索引創(chuàng)建的方式:
ALTER TABLE `table_name` ADD FULLTEXT `column`;
按照索引數(shù)量劃分
如果一個(gè)索引加在一個(gè)字段上,那么它就是一個(gè)單列索引。但是我們也可以將多個(gè)字段聯(lián)合起來(lái)一起創(chuàng)建一個(gè)索引,這種索引就被稱(chēng)為“聯(lián)合索引”,也叫多列索引或者組合索引。為了方便起見(jiàn)。我們之后都統(tǒng)一叫做“聯(lián)合索引”。
注:多個(gè)單列索引并不能被稱(chēng)為組合索引
之后,我們會(huì)單獨(dú)地詳細(xì)講講這個(gè)聯(lián)合索引,以及它產(chǎn)生的一些問(wèn)題,所以本文先不過(guò)多描述。
按照物理實(shí)現(xiàn)劃分
為了幫助大家更好理解,我們現(xiàn)在創(chuàng)建一張表:
create table fancyFamily
(
id int(11) not null auto_increment comment '家庭成員ID',
name varchar(16) not null comment '家庭成員名字',
age int(11) default null comment '家庭成員年齡',
primary key (id)
) engine InnoDB
AUTO_INCREMENT = 10
default charset = utf8;
然后插入一定的數(shù)據(jù)量:
insert into fancyFamily(id, name, age) values (1, 'fancy', 26);
insert into fancyFamily(id, name, age) values (3, '小黃', 25);
insert into fancyFamily(id, name, age) values (8, '大粉', 50);
insert into fancyFamily(id, name, age) values (10, '大黃', 55);
insert into fancyFamily(id, name, age) values (13, '小藍(lán)', 18);
insert into fancyFamily(id, name, age) values (16, '小白', 15);
聚集索引
什么是聚集索引?我們剛剛說(shuō)過(guò),對(duì)于一顆以B+樹(shù)為數(shù)據(jù)結(jié)構(gòu)創(chuàng)建的索引,只有葉子節(jié)點(diǎn)中存放數(shù)據(jù),并且葉子節(jié)點(diǎn)中的數(shù)據(jù)都是按照數(shù)據(jù)庫(kù)表中的數(shù)據(jù)一行一行進(jìn)行連續(xù)排列的:
我們就將一顆滿(mǎn)足于以上數(shù)據(jù)存儲(chǔ)形式的索引形式,稱(chēng)之為“聚集索引”。這里注重的是數(shù)據(jù)的存儲(chǔ)形式,也就是將數(shù)據(jù)存儲(chǔ)在葉子節(jié)點(diǎn)中。
并且由于一張表中的數(shù)據(jù)無(wú)法存放于兩顆不同的B+樹(shù)中。所以一張表也只能有一個(gè)聚集索引。
那我們現(xiàn)在就存在一個(gè)問(wèn)題了:一張表中只能有一個(gè)主鍵,也只能有一個(gè)聚集索引,并且聚集索引還是按照B+樹(shù)中主鍵的數(shù)據(jù)存放形式來(lái)生成的,那么聚集索引不就是主鍵嗎?
還是上文提到的知識(shí)點(diǎn):主鍵是一種約束,用來(lái)完善表結(jié)構(gòu)的數(shù)據(jù)完整性。而聚集索引是一種索引,目的就是將數(shù)據(jù)建立成有序的以便于減少查詢(xún)的時(shí)間復(fù)雜度。它是一種索引,而索引的用途就是用來(lái)提升查詢(xún)效率的。就像是你買(mǎi)房和買(mǎi)車(chē)一樣,它們之間的用途就有著本質(zhì)的區(qū)別。
那“主鍵索引”這個(gè)詞,又是怎么一回事呢?在InnoDB存儲(chǔ)引擎中,一張表一定會(huì)存在一個(gè)主鍵,它是索引能夠被用來(lái)提升效率的基礎(chǔ),只有擁有主鍵才能進(jìn)行排序和查詢(xún)。而只有建立了主鍵,才能夠生成聚集索引這種數(shù)據(jù)存儲(chǔ)形式,所以當(dāng)一張表的主鍵生成式,其對(duì)應(yīng)的聚集索引也便生成了。所以有人也喜歡將聚集索引稱(chēng)為“主鍵索引”。
輔助索引
有了聚集索引,就會(huì)有非聚集索引。除了聚集索引之外的索引都叫做非聚集索引,比如以某非主鍵的字段創(chuàng)建索引后構(gòu)建起來(lái)的索引樹(shù)。它也叫二級(jí)索引或者輔助索引,為了方便起見(jiàn),我們之后都將其稱(chēng)為輔助索引。
那么它和聚集索引的區(qū)別是什么呢?最大的區(qū)別就是聚集索引的葉子結(jié)點(diǎn)是包含表結(jié)構(gòu)的全部行數(shù)據(jù)的,但是輔助索引并不包含。
它依然使用B+樹(shù),只是每個(gè)葉子節(jié)點(diǎn)存儲(chǔ)的是聚集索引所在列的主鍵值:
不同于聚集索引,由于其不按照主鍵進(jìn)行排列檢索,所以一張表中可以存在多個(gè)主鍵索引。
那么,如果我們現(xiàn)在創(chuàng)建了name這個(gè)輔助索引,并且去查詢(xún)小黃這個(gè)名字,它會(huì)怎么操作呢?
select * from workers where name='小黃';
它的查詢(xún)步驟如下:
- 通過(guò)name=’小黃‘這個(gè)條件去輔助索引找到對(duì)應(yīng)的葉子節(jié)點(diǎn)的鍵值
- 找到'小黃'對(duì)應(yīng)的數(shù)據(jù),也就是小黃這行數(shù)據(jù)對(duì)應(yīng)的主鍵
- 通過(guò)這個(gè)主鍵,返回聚集索引
- 通過(guò)聚集索引的主鍵排序,去葉子節(jié)點(diǎn)找到主鍵ID為3的小黃對(duì)應(yīng)的數(shù)據(jù)
也就是說(shuō),如果使用輔助索引,它不會(huì)像主鍵索引那樣直接去葉子節(jié)點(diǎn)找對(duì)應(yīng)的數(shù)據(jù),而是先去葉子節(jié)點(diǎn)找到對(duì)應(yīng)條件的主鍵,再返回聚集索引根據(jù)這個(gè)主鍵去搜索一遍。這種情況我們稱(chēng)為回表:
也就是說(shuō),如果使用了輔助索引,是要比聚集索引多一次樹(shù)的訪問(wèn)和遍歷的。
總結(jié)
以上我們描述了什么是索引、索引的實(shí)現(xiàn)方式、和索引的具體分類(lèi)。通過(guò)索引的功能、數(shù)量、和物理實(shí)現(xiàn)可以區(qū)分為不同的索引,也具體描述了聚集索引和非聚集索引的區(qū)別。
在我身邊有工作了很多年的同事,他們業(yè)務(wù)能力有的一級(jí)棒,但是在講到數(shù)據(jù)庫(kù)的索引也會(huì)出現(xiàn)不夠清晰的劃分和總結(jié),所以我覺(jué)得將這些基礎(chǔ)知識(shí)理清十分重要。
分享標(biāo)題:你知道多少種索引?
標(biāo)題來(lái)源:http://m.fisionsoft.com.cn/article/djsophs.html


咨詢(xún)
建站咨詢(xún)
