MySQL InnoDB索引介紹及優(yōu)化

正文:

一、先說說什么是索引?

索引(index)翻譯為一個(gè)目錄,用于快速定位我們想要找的數(shù)據(jù)的位置。例如:我們把一個(gè)數(shù)據(jù)庫比作一本書,而索引(index)就是書中的目錄,此刻要找到書的某個(gè)感興趣的內(nèi)容,我們一般是不會(huì)整本書翻完再去確認(rèn)該內(nèi)容在哪里,而是通過書的目錄,定位到該內(nèi)容章節(jié)所在頁數(shù),最后直接翻到該頁面

我們來看看在數(shù)據(jù)庫中的索引:
全表掃描 VS 索引掃描
以字典為例,全表掃描就是如果我們查找某個(gè)字時(shí),那么通讀一遍新華字典,然后找到我們想要找到的字
而跟全表掃描相對(duì)應(yīng)的就是索引查找,索引查找就是在表的索引部分找到我們想要找的數(shù)據(jù)具體位置,然后會(huì)到表里面將我們想要找的數(shù)據(jù)全部查出

實(shí)例:在一張學(xué)生表找到一個(gè)名字叫Dev的學(xué)生

MySQL InnoDB索引介紹及優(yōu)化

左邊全表掃描:需要從第一行開始一行行的掃描,直到找到100008行Dev這個(gè)學(xué)生的信息為止,將這個(gè)數(shù)據(jù)返回回來,但有可能該表中還有同名的學(xué)生,因此掃描并沒有結(jié)束,通常全表掃描要找到一個(gè)數(shù)據(jù),是需要將整張表的數(shù)據(jù)遍歷一遍,然后才能確定是否將所有數(shù)據(jù)返回

右邊索引掃描:索引查找是根據(jù)首字母排序找到D開頭的Dev,如果首字母相同,那么再根據(jù)第二個(gè)字母排序找到,以此類推,我們找到ID為100008,然后回表查出ID為100008的數(shù)據(jù)

結(jié)論:因此索引(對(duì)應(yīng)InnoDB)的索引值對(duì)應(yīng)的是主鍵ID

二、如何找到索引對(duì)應(yīng)的值

InnoDB引擎主要根據(jù)
(1)B+tree
(2)二分查找法

MySQL InnoDB索引介紹及優(yōu)化

B+tree: B+樹擁有整棵樹的根節(jié)點(diǎn)、支節(jié)點(diǎn)和頁節(jié)點(diǎn),上層會(huì)存儲(chǔ)下層節(jié)點(diǎn)的管理范圍,直到頁節(jié)點(diǎn)的具體信息

二分查找法:根據(jù)B+樹存儲(chǔ)的各個(gè)節(jié)點(diǎn)的范圍,進(jìn)行比較,逐步縮小范圍,最后定位到頁節(jié)點(diǎn)中我們想要的位置

三、介紹下InnoDB表也是一張索引表

MySQL InnoDB索引介紹及優(yōu)化

如上圖InnoDB表是聚簇表,意思是InnoDB本身是一張大的索引組織表,也是一個(gè)根據(jù)主鍵排序的大索引的B+樹結(jié)構(gòu),我們?cè)贗nnoDB里面另外建立自己想要索引的表的字段

聚簇索引就意味著InnoDB表本身,而我們把這些根據(jù)其他字段排序的索引稱為二級(jí)索引(secondery class)

四、在數(shù)據(jù)庫中如何建立索引

在MySQL中主要建立兩種類型的索引

1.單列索引

create?index?idx_name?on?tb_student(name);  ??????????????索引名????????表名???字段名

2.聯(lián)合索引

create?index?idx_name_age?on?tb_student(name,age);  #索引中先根據(jù)name排序,name相同的情況下根據(jù)age排序

五、索引維護(hù)

首先介紹下什么是索引維護(hù)?這是一個(gè)關(guān)乎性能的重要概念

如果索引所在字段發(fā)生了修改、刪除、插入等操作,那么索引項(xiàng)就會(huì)發(fā)生變化,因此如果不能保證索引的有序,那么就不能索引的準(zhǔn)確與效率,而索引的排序發(fā)生了變化的這個(gè)行為,我們稱為索引維護(hù)    在insert/delete/update操作時(shí),為了維護(hù)索引的排序,數(shù)據(jù)庫會(huì)自動(dòng)的完成索引項(xiàng)的維護(hù),索引的排序,這些行為對(duì)用戶是透明的,感覺不到的    在一個(gè)有索引的表中,創(chuàng)建它時(shí),實(shí)際上還同時(shí)創(chuàng)建了索引排序的表,因此在DML中,插入等操作不再是普通的插入,MySQL將它封裝成了一個(gè)事務(wù),連著索引項(xiàng)的排序表一起操作    因此,我們應(yīng)當(dāng)嚴(yán)格控制表上的索引數(shù)量,否則容易影響數(shù)據(jù)庫的性能

總結(jié)索引維護(hù)如下:

1、索引維護(hù)由數(shù)據(jù)庫自動(dòng)完成
2、插入/修改/刪除每一個(gè)索引行都變成一個(gè)內(nèi)部封裝的事務(wù)
3、索引越多,事務(wù)越大,代價(jià)越高
4、索引越多,對(duì)表的插入和索引字段的修改就越慢

因此可以看出索引并非是越多越好,在工作中也要慎用,尤其對(duì)于寫操作較為頻繁的業(yè)務(wù)

六、如何正確的使用索引?

1、依據(jù)where查詢條件建立索引

eg:  select?a,b?from?tb_test?where?c?=??;  idx_c(c)???->正確    select?a,b?from?tb_test?where?c?=???and?b?=??  idx_cd(c,d)??->正確

2、根據(jù)排序order by ,group by , distinct 字段添加索引

eg:  select?*?from?tb_test?order?by?a;  select?a,count(*)?from?tb_test?group?by?a;  idx_a(a)??->正確    select?*?from?tb_test?order?by?a,b;  idx_a_b(a,b)??->正確    select?*?from?tb_test?order?where?c?=???by?a;  idx_c_a(c,a)??->正確

七、到底哪些字段適合創(chuàng)建索引?

1、字段值的重復(fù)程度,如圖:

MySQL InnoDB索引介紹及優(yōu)化

身份證號(hào)碼由于基本上不可能重復(fù),因此選擇性非常好,而人的名字重復(fù)性較低,選擇性也不錯(cuò), 性別選擇性較差,重復(fù)度非常高

2、選擇性很差的字段通常不適合創(chuàng)建索引,但也有例外

?如:男女比例相仿的表中,性別不適合創(chuàng)建單列索引,如果走索引不如走全表掃描,  ?因?yàn)樽咚饕腎/O開銷更大  ????  ?但如果男女比例極度不平衡,要查詢的又是少數(shù)方,如:理工學(xué)校、IT公司等可以考慮使用索引

3、聯(lián)合索引中選擇性好的字段應(yīng)該排在前面

select?*?from?tab_a?where?gender=??and?name=?  idx_name_gender(name,gender)???->正確

4、聯(lián)合索引可以為單列、復(fù)列查詢提供幫助

idx_smp(a,b,c)  where?a=?;????????????????->正確  where?a=??and?b=?;????????->正確  where?a=??and?c=?;????????->正確?(注:需要MySQL5.6版本以上;在5.5及以前版本,可以對(duì)a字段進(jìn)行索引掃描,但c字段不行????)  where?a=??and?b=??and?c=??->正確

5、合理創(chuàng)建聯(lián)合索引,避免冗余

(a),(a,b),(a,b,c)??????->不可取  (a,b,c)????????????????->正確,可以覆蓋前兩個(gè)

八、再來看看如何在長字段上建立索引呢

首先,在較長的字段上建立索引是非常影響性能的,比如文章等超大varchar或者text字段,如果不是非建不可,一般不推薦,另外對(duì)InnoDB索引單字段(utf8)只能取前767bytes

那么如何處理長字段索引?

主要根據(jù)類型來分別處理:  1、Email類,可以建立前綴索引  mail_addr?varchar(2048)  idx_mailadd(mail_addr(39))???->?正確  解析:由于email郵件類型字段,一般后綴都有較大可能相同,如.com?.cn等等,而前綴相同的可能性較低,且郵箱一般長度較短,因此可以建立前綴索引    2、住址類,分拆字段  home_addr?varchar(2048)  idx_homeadd(home_addr(30))????->錯(cuò)誤,很可能前半段是相同的省市區(qū)街道名    province_add?varchar(1024),city_add?varchar(1024),?district_add?`varchar(1024),lolcal_add?varchar(1024)????--建立聯(lián)合索引或者單列索引?->正確`

九、對(duì)核心SQL索引做覆蓋掃描

對(duì)于最核心的SQL,我們可以考慮使用索引覆蓋,什么是索引覆蓋呢,下面是個(gè)例子

select?name?from?tb_user?where?userid=?  key?idx_uid_name(userid,name)???->覆蓋索引掃描

我們查詢用戶名這種操作頻率非常高,而索引里面又存儲(chǔ)了字段的值,因此在我們做查詢時(shí),name字段的值直接在索引中返回,而不需要回表;還有一個(gè)使用非常廣泛的例子:用戶登陸,我們可以將username password做覆蓋索引,這樣大大提高登陸驗(yàn)證的速度

因此覆蓋索引覆蓋就是將你要查詢的字段和條件字段一起建立聯(lián)合索引,這樣的好處是不需要回表獲取name字段,IO最小,速度塊

十、哪些情況無法使用索引?

1、索引列進(jìn)行數(shù)據(jù)運(yùn)算或者函數(shù)運(yùn)算

eg:  ???where?id+1=10;????->錯(cuò)誤,無法利用到索引  ???where?id=(10-1)???->正確    ???where?year(id)?錯(cuò)誤,無法利用到索引  ???where?col?正確

2、未含復(fù)合索引的前綴字段

idx_abc(a,b,c)  where?b=??and?c=????->錯(cuò)誤,無法利用到索引  正確的建立索引方式(b,c)

3、前綴通配符”_” “%”等

like?'%ttt%'???->錯(cuò)誤,無法利用到索引  like?"ttt%"????->正確

4、where條件使用NOT,,!= 通常也無法使用到索引

5、字段類型不匹配

字段類型并不絕對(duì)匹配時(shí),可能會(huì)導(dǎo)致無法使用索引  a?int(11)?,idx_a(a)  where?a?=?'123'???->錯(cuò)誤,可能導(dǎo)致未知的錯(cuò)誤,這個(gè)跟編碼有關(guān)系  where?a?=?123?????->正確

十一、利用索引做排序操作

以 idx_ab(a,b)索引為例
1、能使用上述索引進(jìn)行排序的操作是:

order?by?a;  a?=?3?order?by?b;  order?by?a,b;  order?by?a?desc?,b?desc;  a?>?5?order?by?a;

2、不能使用索引幫助排序的查詢

order?by?b;?#沒有使用到聯(lián)合索引的第一個(gè)字段    a?>?5?order?by?b;??#一旦前綴操作是一個(gè)range而非=操作,那么就無法利用到索引,  這里?a>5無法利用索引,二聯(lián)合索引的第一個(gè)字段未利用,  因此?order?by?b也無法利用索引查詢    a?in?(1,3)?order?by?b;?#in里面的值沒有建立索引,因此無法利用索引,a未用因此order?by?b也無法使用    order?by?a?asc,?b?desc;?#這里order?by?a?esc是利用了索引,但是b?desc未利用到,因?yàn)閎要和a排序方式一致才可利用到索引

十二、如何確定一個(gè)查詢有沒有走索引,走了哪些索引?

MySQL中自帶命令行工具 explain 來查看一個(gè)sql語句是否了索引

使用方式:

explain?select?*?from?tb_test;

關(guān)注的項(xiàng):

1、type?:?查詢access的方式,表的連接類型  ??????  ??????index?|??索引??  ??????full??|??全表掃描?  ??????ref???|??參照查詢,也就是等值查詢??  ??????range?|??范圍查詢  2、key??:?本次查詢最終選擇使用哪個(gè)索引,NULL為未使用索引  3、key_len?:?選擇的索引使用的前綴長度或者整個(gè)長度  4、rows????:?查詢邏輯掃描過的記錄行數(shù)  5、extra???:?額外信息,主要是指fetch?data的具體方式

總結(jié):索引的本質(zhì)還是提升我們查詢數(shù)據(jù)庫的速度,減少服務(wù)器I/O開銷,提供更穩(wěn)定快捷的服務(wù)

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊6 分享