最近在重構(gòu)自己工作上寫的代碼,重構(gòu)完后也開始考慮要做一些數(shù)據(jù)庫優(yōu)化规揪。其中一項工作就是重新審視之前設(shè)置的索引是否合理桥氏。于是這周都在反復看《極客時間》上面的Mysql實戰(zhàn)的專欄,先惡補下索引方面的基礎(chǔ)知識猛铅,本文在此做一些記錄识颊。
0、背景
本文以Mysql中的InnoDB的使用為例
1奕坟、索引解決的問題
索引,可以理解為一本書的目錄清笨,它要解決的是提高查詢效率的問題月杉。當我們要查找一本書的某個內(nèi)容時,如果沒有目錄抠艾,就得一頁一頁去找苛萎,效率非常低。但當我們有一個目錄時,直接根據(jù)要查找的關(guān)鍵詞腌歉,就可以在目錄上搜尋主題蛙酪,從而更快地搜尋到關(guān)心的內(nèi)容
2、索引的數(shù)據(jù)結(jié)構(gòu)
既然是為了方便查找數(shù)據(jù)翘盖,那么我們就會根據(jù)數(shù)據(jù)的特性桂塞,找到出能提高其讀寫效率的數(shù)據(jù)模型,這里我們簡單說三種:
- 有序數(shù)組
該模型的特點是 靜態(tài)馍驯、順序 存儲阁危,不適合需要經(jīng)常插入的場景 - 哈希表
適合等值查詢的場景,經(jīng)常見的就是key-value汰瘫;由于插入的數(shù)據(jù)都是無序的狂打,哈希表在進行范圍查詢或有序查詢時,就不太適用混弥; - 二叉樹
出現(xiàn)的原因主要是讓查詢盡可能少的訪問磁盤趴乡;其特點是leftChild < parent< rightChild,所以樹也是一個有序的結(jié)構(gòu)蝗拿;
3晾捏、索引的類型
索引類型分兩種,根據(jù)其索引樹葉子節(jié)點的內(nèi)容蛹磺,區(qū)分成兩類粟瞬,一個是普通索引,一個是主鍵索引萤捆;普通索引又稱為二級索引裙品,為啥稱為二級索引呢?大家可以看下圖
例如我對表A建立了一個普通索引k市怎,Mysql中便會建立兩顆索引樹,一個是主鍵索引樹辛慰,一個是普通索引樹区匠,如上圖。
那么大家可能會問帅腌,基于主鍵索引 和 普通索引的查詢有什么區(qū)別驰弄?
我們用兩條Mysql語句進行說明。
如果是基于主鍵索引速客,select * from T where id = 500, 它只用搜索主鍵索引樹戚篙;
如果是基于普通索引,select * from T where k = 3溺职,它的搜索過程如下:
- 先到K索引樹上找到k = 3的值
- 找到后岔擂,取出葉子節(jié)點的值位喂,得知主鍵為300,則再去主索引樹搜尋ID = 300的節(jié)點乱灵,再取出數(shù)據(jù)塑崖;
從此可以得出,普通索引要比主鍵索引的查詢多查一次索引樹痛倚,這個過程我們稱為 回表规婆,我們一般都建議,用主鍵查詢状原,就是為了減少回表的過程聋呢;
4、建表時最好用自增Id主鍵颠区,為什么削锰?
這個問題,主要是從性能和存儲的方面來說的毕莱。首先我們先看下索引的維護過程
4.1 索引維護過程
我們知道器贩,當插入新數(shù)據(jù)時,索引也是要相應維護朋截,如果我們新插一條數(shù)據(jù)蛹稍,而且是用自增ID的話,基本上數(shù)據(jù)是在最新ID的后面部服,整顆樹還是保持有序的唆姐,不需要變動,但如果現(xiàn)插入一條數(shù)據(jù)廓八,例如在id = 500前插入一個id = 400的值奉芦,這會就需要調(diào)整樹結(jié)構(gòu),把值插進去剧蹂。声功。就很有可能會出現(xiàn)數(shù)據(jù)頁滿了的情況,導致進行分頁宠叼,這樣Mysql就會把一部分數(shù)據(jù)挪過去先巴,導致性能受到影響。同樣的冒冬,如果我們刪除了一些數(shù)據(jù)伸蚯,數(shù)據(jù)庫也會做合并。
4.2 場景
比如現(xiàn)在有一個用戶表简烤,用戶表中有一個唯一標志剂邮,身份證。
我們假設(shè)用身份證做主鍵乐埠,會出現(xiàn)兩個問題:
- 一個主鍵就會占用至少二十個字節(jié)抗斤,后邊新建普通索引的時候,該普通索引上的每一個葉子節(jié)點丈咐,至少有二十個字節(jié)瑞眼,整體算下來會占較大空間。
- 用身份證做主鍵并不能保證有序插入棵逊,插入時可能產(chǎn)生數(shù)據(jù)的挪動伤疙,從而降低查詢性能
當然也有些場景是適合用業(yè)務字段作為主鍵的,什么情況呢辆影?
當該字段是表中的唯一索引 徒像,典型場景就是 KV場景
既然表中沒有其他索引,也不用考慮葉子節(jié)點的大小了蛙讥,直接將其設(shè)成主鍵锯蛀,這樣也不用回表查詢
5、索引的其他概念
在使用索引的時候次慢,我們還經(jīng)常聽到這樣一些概念,例如 覆蓋索引旁涤,前綴索引,索引下推迫像,下面我們再分別介紹一些這些概念是什么
5.1 覆蓋索引
覆蓋索引的定義就是 定義的索引已經(jīng)覆蓋了查詢需求劈愚,該索引查詢出的結(jié)果已經(jīng)滿足需求
還是第三節(jié)的這個圖,k是普通索引闻妓,ID是主鍵菌羽,我們看看下列sql語句
select ID from T where k between a and b
這里,通過這句sql得出的查詢結(jié)果已經(jīng)能滿足查詢到ID的需求了由缆,不需要再回表(回到主索引樹)查詢一次注祖,索引k 就 被稱為 覆蓋索引
由上得知,覆蓋索引主要是為了減少索引樹的搜索次數(shù)犁功;如果有些字段查詢非常高頻氓轰,可以考慮建立一些索引,覆蓋其查詢需求浸卦;
5.2 前綴索引
前綴索引在百度上的定義多為 用聯(lián)合索引的最左N個字段或者字符串的最左M個字符進行查詢的索引署鸡。在這里大家可能會一直冒問號了,為什么是最左限嫌?為什么最左N個字段就可以加速查詢靴庆?
首先,要知道聯(lián)合索引的存儲結(jié)構(gòu)怒医,索引項按照索引定義中出現(xiàn)的字段項進行排序炉抒,比如我現(xiàn)在給用戶表里面建立了(name,age)的聯(lián)合索引稚叹,建立后的數(shù)據(jù)存儲結(jié)構(gòu)可看下圖:
圖片來源極客時間焰薄,侵刪
可以看到拿诸,索引項是按照我們的定義(姓名,年齡)排好序的塞茅,當我們?nèi)ゲ樵儭畯埲臅r候亩码,他會迅速定位到ID4,然后向后遍歷野瘦;
當然描沟,我們也可以查姓張的人,這樣我們就直接定位到了ID3鞭光,于是這里我們可以看到吏廉,不只是索引的全部定義,只要用到索引的最左N個字段/前綴惰许,就可以加速查詢
5.3 索引下推
索引下推席覆,其實在索引遍歷過程中,對索引包含的字段做判斷啡省,過濾不符合條件的記錄娜睛,減少回表次數(shù)。
比較好玩的是卦睹,Mysql在5.6版本才開始支持該功能畦戒,下推的過程可以看下圖
圖片源自極客時間,侵刪
六结序、建立索引的原則
根據(jù)上面的概念介紹障斋,我們其實可以總結(jié)出,建立索引時要遵循的一些原則:
- 最左前綴(最高頻的放到最左邊)
- 索引的復用能力(若調(diào)整字段順序徐鹤,能夠少一個索引垃环,則優(yōu)先使用該順序)
- 考慮空間和性能