什么是索引?
索引的本質(zhì)是獨(dú)立于數(shù)據(jù)本身之外的一種數(shù)據(jù)結(jié)構(gòu),它是基于某個(gè)或某些字段建立一堆有序且可快速高效查找的數(shù)據(jù),類似字典中首字母索引,或圖書館中圖書的索引;
索引存在核心是為了過濾數(shù)據(jù)+檢索,同時(shí)能快速定位數(shù)據(jù)的實(shí)際物理位置。其根本作用是要減少查詢時(shí)的邏輯和物理IO或CPU運(yùn)算,索引在運(yùn)行中很多時(shí)候會(huì)加載到緩存中,通過索引定位數(shù)據(jù)在磁盤上的物理地址,進(jìn)行數(shù)據(jù)獲取。
在mysql上索引最常用的數(shù)據(jù)結(jié)構(gòu)就是B+樹,類似平衡二叉樹的平衡多叉樹,它的葉子節(jié)點(diǎn)會(huì)指向數(shù)據(jù)相關(guān)物理地址,從而實(shí)現(xiàn)快速定位數(shù)據(jù);其樹的高度就決定了定位數(shù)據(jù)位置需要IO次數(shù),假設(shè)總的數(shù)據(jù)量為N,樹的每個(gè)節(jié)點(diǎn)的數(shù)據(jù)量為m,這樹高度就為log(m+1)N,所以通過索引只需要log(m+1)N次IO加上計(jì)算就可以定位到數(shù)據(jù),這對(duì)比整個(gè)數(shù)據(jù)表遍歷會(huì)是一個(gè)極大的提升(完全不在一個(gè)數(shù)量級(jí)上)。

具體索引的原理,推薦看一下美團(tuán)點(diǎn)評(píng)團(tuán)隊(duì)的一篇文章:索引原理和慢查詢優(yōu)化
維護(hù)索引需要的什么代價(jià)?
1. 索引需要占用額外儲(chǔ)存空間,包含內(nèi)存空間,也就意味著額外的IO操作
2. 數(shù)據(jù)表每一次更新,需同步調(diào)整相關(guān)的索引,額外的CPU和IO負(fù)載。
3. 當(dāng)數(shù)據(jù)量增加到一定程度,索引本身的數(shù)據(jù)結(jié)構(gòu)會(huì)特別龐大,維護(hù)起來成本會(huì)非常高
之前公司有做過數(shù)據(jù)遷移,千萬級(jí)別的表,在有索引插入的情況下,執(zhí)行了幾個(gè)小時(shí),但去掉索引,遷移完成后重建索引,不到1小時(shí)就完成
索引類型介紹
聚集索引和非聚集索引
在存儲(chǔ)結(jié)構(gòu)上不同可以將索引分為聚集索引和非聚集索引;一般來說,基于聚集索引的插入會(huì)特別快,碎片化數(shù)據(jù)也會(huì)少很多,單條查詢也會(huì)快很多。
聚集索引
聚集索引單表上是唯一的,一個(gè)表最多只能有一個(gè)聚集索引。聚集索引在物理存儲(chǔ)上有有序的,即代表數(shù)據(jù)行在磁盤的是按照聚集索引的順序存儲(chǔ)的。聚集索引的葉子節(jié)點(diǎn)是數(shù)據(jù)行,即在聚集索引的葉子節(jié)點(diǎn)上,直接可以找到數(shù)據(jù)本身。
非聚集索引
非聚集索引可以在單表存在多個(gè)邏輯上有序,物理上無序非聚集索引的葉子節(jié)點(diǎn)上存儲(chǔ)的是索引塊,里面對(duì)應(yīng)的是相關(guān)數(shù)據(jù)的物理地址,并不是數(shù)據(jù)本身
類型劃分
【普通索引】
【唯一索引】 值唯一,可以為null
【主鍵索引】 值唯一,不可為null,且每個(gè)表唯一
【組合索引】 多個(gè)字段的組合索引
【全文索引】 富文本,簡易搜索引擎使用
數(shù)據(jù)結(jié)構(gòu)劃分
B+樹
B-樹
哈希索引
使用索引需要注意什么?(閱讀劃重點(diǎn))
1. 頻繁更新或讀少寫多的表不適合過多索引
2. 最好的索引是能在平衡樹葉子節(jié)點(diǎn)上快速定位到數(shù)據(jù),過濾掉不在查詢區(qū)間的數(shù)據(jù),這就意味重復(fù)性低的字段更適合作為索引,比如自增鍵、時(shí)間戳、唯一字段等;不適合索引的字段比如少量狀態(tài)、邏輯刪除標(biāo)記字段、性別等
3. 索引列切記不能有空值(null),請(qǐng)?jiān)O(shè)置字段not null+默認(rèn)值,null會(huì)影響索引效率
4. 數(shù)字字段比字符字段更適合索引,索引的核心在排序,數(shù)字的比較CPU運(yùn)算量更少,需要的內(nèi)存、邏輯IO和物理IO也更少,建立字段時(shí)如果可以盡量使用數(shù)字類型
5. 索引的建立和查詢是基于字段的排序,如果字符字段過長,會(huì)出現(xiàn)極大增加排序負(fù)荷,并且會(huì)導(dǎo)致額外IO,使用索引字段請(qǐng)注意調(diào)整字段長度,不定長度或過長字段不適合索引,相反較短的字段就更適合;而一般數(shù)字字段都比字符字段短,占用空間也越少
6. 每個(gè)表最好都要有一個(gè)主鍵,使用主鍵在做表更新和刪除會(huì)更加方便和安全,同時(shí)方便擴(kuò)展、松耦合系統(tǒng);在innodb引擎上,建議使用于業(yè)務(wù)無關(guān)的自增鍵來做主鍵,因?yàn)閕nnodb會(huì)默認(rèn)使用自增鍵來作為表的聚集索引(見索引類型章節(jié)),可以增加更新效率,減少數(shù)據(jù)碎片;自增主鍵還有特點(diǎn)是有時(shí)序,在業(yè)務(wù)邏輯上很多時(shí)候很有用。
7. 組合索引是指在業(yè)務(wù)查詢邏輯,經(jīng)常一起出現(xiàn)的字段組合成一個(gè)索引,會(huì)更加高效;列重復(fù)值較多的、單獨(dú)索引效率低的字段也可考慮使用組合索引(比如一級(jí)分類和二級(jí)分類的組合索引);組合索引的建立要盡量對(duì)查詢形成索引覆蓋,其前置索引字段一定是使用要是最頻繁的;
8. 組合索引要注意最左前綴匹配原則,組合索引的原理是將多個(gè)字段按照提供的順序組合后的整體作為索引排序;在mysql中,引擎會(huì)一直向右匹配直到遇到范圍查詢(>、<、between、like)就停止匹配,所以組合索引(a,b,c)可以匹配(a=1,b>1)的查詢,但匹配不到(a=1,b>1,c=1),其中c=1會(huì)進(jìn)行單獨(dú)查詢;(a=1,b=1)會(huì)用到索引,(b=1,c=1)會(huì)用不到。同樣模糊查詢中(a like 'xxx%')會(huì)用到a索引,但(a like '%xxxx')用不到;
9. 盡量的擴(kuò)展索引,不要新建索引。根據(jù)實(shí)際業(yè)務(wù)情況,調(diào)整索引時(shí),如無必要,不要新增索引,考慮擴(kuò)展組合索引
10. 索引列請(qǐng)保持干凈,不要有任何計(jì)算,會(huì)導(dǎo)致索引無效;比如unix_timstamp()-[timefield] <?180,替換為[timefield]>unix_timstamp()-180
11. 反向查詢會(huì)使索引無效,比如a<>1,not in查詢,not in使用not exist替代
12.?使用組合索引字段是可以亂序的,查詢引擎會(huì)根據(jù)索引來調(diào)整順序。(a,b,c)組合索引,會(huì)匹配c>1 and a=2? and b=3的情況
13. 使用union替代or,在某些情況or查詢會(huì)導(dǎo)致映射不到索引,比如select * from table where a=1 or b=2 替換為 select * from table where a=1 union select * from table where b=2,這樣就可以使用a,b兩個(gè)索引;同一字段的or查詢使用in替代。在新版的mysql中(5.6?)查詢引擎已可自動(dòng)轉(zhuǎn)化,但遇到復(fù)雜sql時(shí),可能會(huì)轉(zhuǎn)換失敗
14. 如果查詢用到多個(gè)索引的時(shí),可以手動(dòng)將過濾掉更多數(shù)據(jù)的索引字段放到前面,可以優(yōu)化查詢
15. 大表比如千萬級(jí)別的表調(diào)整索引會(huì)很長時(shí)間鎖表,盡量在一開始考慮好索引建立
16. 索引越多,維護(hù)成本也越高;不要過度索引,能在業(yè)務(wù)邏輯層做優(yōu)化的,就不要放到數(shù)據(jù)庫上來,特別是在高并發(fā)系統(tǒng)中,數(shù)據(jù)庫一般都是瓶頸,請(qǐng)解放數(shù)據(jù)庫。索引的使用的大的方向是:宜短不宜長,宜少不宜多
17. 技術(shù)的事沒有絕對(duì)標(biāo)準(zhǔn),可以考慮實(shí)際業(yè)務(wù)需求添加需要索引,但添加之前要考慮上面的事項(xiàng)。