MySQL的索引入門真的很難嗎
經(jīng)常在開發(fā)中碰到同事說募谎,數(shù)據(jù)查詢好慢扶关,第一個反應(yīng)就是給表加個索引。從而引發(fā)想去探索下我們常說的索引究竟是什么数冬?難道只需要加個索引就能解決數(shù)據(jù)庫查詢問題嗎节槐?
帶著這個問題我們開始探究MySQL中的索引究竟是什么,它能幫助我們做些什么吉执。
索引存在的意義
在現(xiàn)有程序業(yè)務(wù)中疯淫,數(shù)據(jù)庫作為存儲的重要一環(huán),不可或缺戳玫,而對于數(shù)據(jù)庫的操作無外乎是增刪改查,但隨著數(shù)據(jù)量的增加未斑,數(shù)據(jù)庫的性能就成為最重要的一環(huán)咕宿,數(shù)據(jù)查詢不能慢,數(shù)據(jù)查詢一慢,用戶體驗(yàn)就會差府阀。
如何在保證數(shù)據(jù)存儲中的增刪改查效率呢缆镣?就成了一個必不可少的設(shè)計(jì)。
在Mysql這樣的數(shù)據(jù)存儲中總是少不了一個東西--->索引试浙,索引就類似于我們看書的目錄董瞻,使用書籍的目錄可以幫助快速的定位到知識點(diǎn)的頁數(shù),而索引也是同樣的目的田巴,快速檢索到數(shù)據(jù)钠糊。
那就可以總結(jié)出索引的目的:提高數(shù)據(jù)的檢索速度。
索引的類型
既然索引有提高檢索的速度壹哺,那就給數(shù)據(jù)庫的查詢操作都加上索引抄伍,讓他們飛快的運(yùn)行,這事還真不能急管宵,為啥截珍?數(shù)據(jù)庫的索引種類有好多種,萬一索引用的不對箩朴,引發(fā)的不是加快數(shù)據(jù)庫的運(yùn)行岗喉,而是眾多的慢查詢會將整個數(shù)據(jù)庫拖垮。
用于提高讀寫效率的數(shù)據(jù)結(jié)構(gòu)種類這么多炸庞,那我們來了解下數(shù)據(jù)庫中常見的索引類型都有哪些沈堡。
索引類型 | 哈希索引 | 二叉樹 | 跳表 | B+Tree |
---|
哈希索引
哈希索引簡單來說就是Key-Value模型,我們只要通過給定的Key就可以查找到對應(yīng)的Value燕雁,十分快速方便诞丽。
不過你要明白哈希索引是通過哈希函數(shù)對Key進(jìn)行計(jì)算,換算成數(shù)據(jù)存儲位置拐格,隨著數(shù)據(jù)量的增加僧免,不可避免會出現(xiàn)不同的Key經(jīng)過哈希函數(shù)計(jì)算后出現(xiàn)相同的數(shù)據(jù)存儲位置。
這種情況怎么解決呢捏浊?業(yè)界通用的方式是當(dāng)出現(xiàn)位置一樣的數(shù)據(jù)結(jié)果懂衩,會在該結(jié)果后面鏈接一個鏈表,將相同的數(shù)據(jù)放入到鏈表中金踪。
更進(jìn)一步當(dāng)相同位置中的數(shù)據(jù)越來越多浊洞,查詢數(shù)據(jù)時會將鏈表中的數(shù)據(jù)遍歷,速度也是慢胡岔,這時候可以采用將鏈表進(jìn)行樹化法希,二叉樹的查找速度還是很快的。
??圖是數(shù)據(jù)舉例說明:
哈希索引只適合用來查找等值的數(shù)據(jù)靶瘸,而不是適合范圍索引苫亦,排序等操作毛肋。常見的哈希索引是在Redis中。
二叉樹
數(shù)據(jù)結(jié)構(gòu)中存在樹數(shù)據(jù)結(jié)構(gòu)屋剑,雖然樹的結(jié)構(gòu)多種多樣润匙,但是常用的數(shù)據(jù)結(jié)構(gòu)是二叉樹,二叉樹是擁有兩個分叉的樹唉匾,分別為左子節(jié)點(diǎn)與右子節(jié)點(diǎn)孕讳。以此類推,動物中有八爪魚巍膘,同樣的也存在八叉樹厂财,你可以想象八叉樹是什么樣子。
二叉樹的特點(diǎn)是典徘,左節(jié)點(diǎn)的值<父節(jié)點(diǎn)<右節(jié)點(diǎn)蟀苛。如果要查找到一個值就可以按照子節(jié)點(diǎn)的順序進(jìn)行查找.
隨著數(shù)據(jù)量的增大,二叉樹的高度也會主鍵遞增逮诲,數(shù)據(jù)庫存儲的數(shù)據(jù)并不是都放到內(nèi)存中帜平,而是要放到磁盤上,磁盤的訪問速度是比內(nèi)存慢幾十倍梅鹦。
現(xiàn)在假如一個樹高30,每次搜索樹一次就需要訪問一次硬盤,一次訪問磁盤速度假設(shè)是10ms裆甩,樹高30至少需要訪問磁盤30次才能獲取到數(shù)據(jù),30*10=300ms齐唆。
如果數(shù)據(jù)更多嗤栓,樹高到100,獲取一次數(shù)據(jù)成本就很高了箍邮。
為了解決這個問題茉帅,可以使用N叉樹的方式來降低樹的高度,減少訪問磁盤的次數(shù)锭弊,這樣就能提高效率堪澎。
跳表
跳表是建立在多層級鏈表上的數(shù)據(jù)結(jié)構(gòu),通過一層層的鏈表查詢就提高了檢索數(shù)據(jù)的效率味滞。
B+Tree
Mysql中索引的實(shí)現(xiàn)是建立在數(shù)據(jù)庫引擎上的樱蛤,而在Mysql中有多個數(shù)據(jù)庫引擎,常用的數(shù)據(jù)庫引擎是InnoDB.
InnoDB引擎索引實(shí)現(xiàn)是使用B+Tree索引模型剑鞍,其實(shí)還有一種BTree模型昨凡,B+Tree是建立在BTree基礎(chǔ)上發(fā)展的。
B+Tree可以認(rèn)為是BTree的改進(jìn)版本:
注意子節(jié)點(diǎn)與葉子節(jié)點(diǎn)是不同的概念蚁署。把沒有子節(jié)點(diǎn)的節(jié)點(diǎn)叫做葉子節(jié)點(diǎn)
- 在B+Tree中子節(jié)點(diǎn)只存儲索引便脊,而在B樹中是存儲數(shù)據(jù)的。
- B樹中的葉子節(jié)點(diǎn)并不需要使用鏈表連串聯(lián)形用,而B+樹中是用鏈表連接起來的就轧。
- B+樹中的葉子節(jié)點(diǎn)存儲數(shù)據(jù).
數(shù)據(jù)庫中每一個索引都能對應(yīng)到一顆B+樹证杭,一個表是可以存在多個B+樹田度。
不管是B+Tree還是BTree都是利用多叉樹(該樹有多少叉是根據(jù)頁的大小進(jìn)行計(jì)算好的妒御,索引會涉及到新增刪除,同樣的就會涉及到頁的分裂與合并)镇饺,保證不把所有的索引數(shù)據(jù)放入到內(nèi)存上乎莉,降低磁盤的訪問次數(shù)加快數(shù)據(jù)訪問。
索引的分類
Mysql中常用Innodb引擎,組織數(shù)據(jù)庫索引的方式就是B+Tree奸笤。
B+Tree是索引組織表惋啃,那在B+Tree有多少種索引的類型呢?
從不同的方向劃分可以劃分為不同的類型监右。
功能上區(qū)分
主要為普通索引边灭,主鍵索引,唯一索引健盒,前綴索引绒瘦,全文索引,哈希索引扣癣。
普通索引
普通索引就是我們常用的索引創(chuàng)建-> 創(chuàng)建單個索引惰帽,相關(guān)語句如下
alter table table_name add index index_name(column);
drop index index_name on table_name;
ALTER TABLE table_name DROP INDEX index_name
主鍵索引
主鍵索引是在普通索引的基礎(chǔ)上增加兩種約束條件分別為唯一和不能為空。主鍵索引在Innodb中用來維護(hù)索引組織的性質(zhì)父虑,所以该酗,在使用Innodb引擎時,建議你的表都設(shè)置主鍵士嚎。
創(chuàng)建主鍵可以在創(chuàng)建的表的時候指定 primary key('id'),也可以創(chuàng)建聯(lián)合主鍵primary key ('id','name').
創(chuàng)建主鍵的相關(guān)SQL
# 當(dāng)表里面沒有主鍵索引時呜魄,增加主鍵索引
ALTER TABLE table_name ADD PRIMARY KEY ( `id` )
# 刪除主鍵索引
ALTER TABLE table_name DROP INDEX name_index
唯一索引
唯一索引時在普通索引的基礎(chǔ)上增加唯一的約束,在插入相關(guān)數(shù)據(jù)時莱衩,會檢查該索引數(shù)據(jù)是否已經(jīng)存在數(shù)據(jù)庫中爵嗅。
使用下面的創(chuàng)建語句創(chuàng)建:
# 創(chuàng)建唯一索引
ALTER TABLE table_name ADD UNIQUE (`column`)
# 刪除索引
drop index index_name on table_name;
前綴索引
字符串在編程中經(jīng)常遇到的,比如常用的郵箱,一些業(yè)務(wù)場景中需要對某些字符串的前綴進(jìn)行匹配膳殷。
這就涉及到一個問題操骡,不能使用索引的話,就只能進(jìn)行全表掃描赚窃。數(shù)據(jù)量一大册招,該方式就會成為性能的瓶頸。
數(shù)據(jù)庫中的前綴索引就是解決字符串前綴匹配的問題勒极。
# 創(chuàng)建前綴索引
alter table table_name add index index_name(columns(6));
# 刪除索引
drop index index_name on table_name;
# 怎么計(jì)算前綴索引設(shè)計(jì)幾個字符 使用下列語句進(jìn)行估算
select count(distinct 列名)/count(*)as a,COUNT(DISTINCT left(列名,100)) as b, COUNT(DISTINCT left(列名,110)) as c from 表名
前綴索引有一個缺點(diǎn)就是無法使用覆蓋索引的優(yōu)化是掰,必須回表查詢。
全文索引
全文索引是用來解決Mysql中文本匹配慢的問題辱匿,常使用like模糊搜索%內(nèi)容%键痛,沒法用到前面列舉的索引炫彩,這時候就可以嘗試使用全文索引來解決該問題。
相關(guān)SQL文件看下??
create fulltext index table_name
on index_name(column,column);
alter table table_name
add fulltext index index_name(column,column);
注意一點(diǎn)的是全文索引是有自己的匹配語法絮短,使用match和against關(guān)鍵字來進(jìn)行匹配??江兢。
select * from table_name where match(column,column) against('xxx xxx');
從索引個數(shù)上區(qū)分
從個數(shù)區(qū)分就是該索引郵幾個列組成。當(dāng)有多個列構(gòu)成就是聯(lián)合索引
- 單個索引:只有一列創(chuàng)建的索引
- 聯(lián)合索引:多列聯(lián)合組成的索引丁频。
單個索引的介紹不用多說杉允,這里主要說下聯(lián)合索引。
聯(lián)合索引
我們知道B+Tree樹這種形式的索引結(jié)構(gòu)是可以使用最左前綴席里,來定位記錄叔磷。十分恰當(dāng)?shù)穆?lián)合索引就需要使該規(guī)則才能發(fā)揮出強(qiáng)大的作用。
最左前綴
順便引出的問題就是我們在創(chuàng)建聯(lián)合索引的時候應(yīng)該怎么安排索引內(nèi)的字段順序奖磁?
當(dāng)當(dāng)前表是新建立的改基,還沒有其他索引可以根據(jù)業(yè)務(wù)需求進(jìn)行直接創(chuàng)建;如果表中已經(jīng)存在其他索引咖为,那可以通過調(diào)整順序幫助減少索引的創(chuàng)建秕狰。
每次創(chuàng)建一個新的索引,就會增加一部分的索引存儲空間案疲,隨著數(shù)據(jù)量的增加封恰,索引的存儲也會暴漲,所以在創(chuàng)建索引時褐啡,都需要考一個空間占用的原則诺舔。
當(dāng)有一個大字段和小字段組合成聯(lián)合索引時,大字段索引放在聯(lián)合索引的前面备畦。
比如現(xiàn)在需要根據(jù)郵件和年齡查詢數(shù)據(jù)低飒,但還有根據(jù)age以及email單獨(dú)查詢的需求。
一般第一個反應(yīng)就是創(chuàng)建三個索引懂盐,age,name,(name,age)/(age,name)褥赊。
而一般email的長度是大于age的,在有最左前綴的原則下莉恼,聯(lián)合索引第一個字段單獨(dú)查詢是可以使用索引拌喉。則這里選擇創(chuàng)建的索引就是age,name,(name,age)。
從磁盤角度區(qū)分
看一些數(shù)據(jù)庫資料總是聚簇索引俐银,非聚簇索引尿背,這兩種方式跟主鍵索引,普通索引又有什么區(qū)別捶惜?
聚簇索引田藐,非聚簇索引
其實(shí)就是一類內(nèi)容,只是根據(jù)分類的方式不同,叫的名字不同而已汽久。聚簇索引與非聚簇索引是指在磁盤上對數(shù)據(jù)的組織結(jié)構(gòu)不同鹤竭。
聚簇索引可認(rèn)為是磁盤將實(shí)際數(shù)據(jù)按照定物理地址進(jìn)行順序存放,并且與索引的順序是一致的景醇。那么當(dāng)索引是相鄰的臀稚,對應(yīng)的數(shù)據(jù)一定也是按照相鄰的順序存放。
磁盤對于順序讀取速度比磁盤隨機(jī)讀取的速度要快很多啡直;正因?yàn)榫鄞厮饕前凑瘴锢眄樞蜻M(jìn)行存儲烁涌,那一個表只能有一個聚簇索引苍碟,該索引在Mysql中是主鍵索引酒觅,當(dāng)然主鍵索引也是可以包含多個列的。
其他類型的索引都是被稱為非聚簇索引微峰。
)
使用非聚簇索引查詢數(shù)據(jù)舷丹,目的是先查到對應(yīng)的聚簇索引也就是主鍵索引。通過主鍵索引從而查詢到對應(yīng)的數(shù)據(jù)蜓肆。在這個過程中還涉及到兩個技術(shù)颜凯,分別為回表以及索引下推。
回表
回表很見到就是通過其他索引查到對應(yīng)的主鍵值仗扬,再使用主鍵值去表里面再檢索一遍數(shù)據(jù)症概。
那有沒有不用回表?有的早芭,在查詢的數(shù)據(jù)的時候彼城,查詢的索引中已經(jīng)包含要的字段,就不需要再使回表使用主鍵查詢數(shù)據(jù)退个,這句話可能有點(diǎn)蒙募壕,看下SQL你就明白了。
# 設(shè)置userid為普通索引
# 不使用覆蓋索引语盈,需要回表查詢
select * from user where userid=1
# 使用了覆蓋索引舱馅,這是因?yàn)閡serid的索引列上葉子節(jié)點(diǎn)就是存儲的主鍵id,不需要再回表
select ID from user where userid=1;
# 使用聯(lián)合索引也是可以做到的(userid,name)做一個聯(lián)合索引,索引列上有對應(yīng)的值刀荒,則不需要再回表查詢
select name from user where userid =1
索引下推
在Mysql5.6版本以前代嗤,查詢到的數(shù)據(jù)每條都需要重新回表查詢一次,而在5.6版本增加索引下推技術(shù)后缠借,可以直接在索引列中過濾掉不需要數(shù)據(jù)干毅,減少回表的次數(shù)。
注意該技術(shù)需要使用范圍是聯(lián)合索引(age,sex)
select * from user where 10<age and age>20 and sex=1;
首先使用索引查詢到年齡滿足第一條大于10小于20的人烈炭,同時索引列可以直接進(jìn)行判斷該用戶性別是不是滿足1(男性)溶锭,是就繼續(xù),不是該條記錄過濾掉符隙。
而在5.6以前滿足大于10小于20后趴捅,根據(jù)讀取到主鍵數(shù)據(jù)再進(jìn)行對比垫毙。回表的次數(shù)自然比使用索引下推技術(shù)版本多拱绑。
總結(jié)
本章從一個小問題引發(fā)了對索引的探索综芥,包含對現(xiàn)有的數(shù)據(jù)庫中常用的幾個索引技術(shù)進(jìn)行介紹,明白為什么Mysql會選擇B+Tree作為索引的檢索方式猎拨,并通過此方式梳理了Mysql中現(xiàn)有的索引技術(shù)膀藐。
希望這邊索引文章能幫助到你對索引的理解。