數(shù)據(jù)庫(kù)的索引與書(shū)籍的索引/目錄類(lèi)似,有了索引就不需要翻整本書(shū),數(shù)據(jù)庫(kù)可以直接在索引中查找,在索引中找到條目之后,就可以直接跳轉(zhuǎn)到目標(biāo)文檔的位置,這就能使查找速度提高幾個(gè)數(shù)量級(jí)。不使用索引的查詢稱(chēng)為全表掃描,也就是說(shuō)服務(wù)器必須查找完一整本書(shū)才能找到查詢結(jié)果,對(duì)于大集合來(lái)說(shuō),應(yīng)該盡量避免全表掃描,否則效率非常低。建立索引是保證數(shù)據(jù)庫(kù)性能、保證小程序體驗(yàn)的重要手段。我們應(yīng)為所有需要成為查詢條件的字段建立索引。
我們可以在云開(kāi)發(fā)控制臺(tái)數(shù)據(jù)庫(kù)標(biāo)簽頁(yè)對(duì)每個(gè)集合的字段添加索引,設(shè)置索引的屬性為唯一或非唯一,排序方式為升序或降序,也能查看該索引所占的空間和命中次數(shù)。索引是一個(gè)文件,它是要占據(jù)物理空間的,因此我們可以留意不要過(guò)度索引浪費(fèi)空間,而命中次數(shù)也可以用于判斷索引是否有效。
云開(kāi)發(fā)數(shù)據(jù)庫(kù)會(huì)給每個(gè)集合默認(rèn)建立_id索引和_openid索引。_id索引在我們進(jìn)行db.collection('集合名').doc("_id值")的請(qǐng)求時(shí)就會(huì)命中。而當(dāng)我們?cè)趙here里添加_openid為查詢條件,就會(huì)命中_openid索引,在小程序端進(jìn)行db.collection('集合名')查詢時(shí),由于自帶默認(rèn)條件_openid為用戶的openid,因此在小程序端查詢都會(huì)命中_openid索引。
單字段索引是最常見(jiàn)的索引,它不會(huì)自動(dòng)創(chuàng)建。對(duì)需要作為查詢條件篩選的字段,我們可以創(chuàng)建單字段索引。如果需要對(duì)嵌套字段進(jìn)行索引,那么可以通過(guò) "點(diǎn)表示法" 用點(diǎn)連接起嵌套字段的名稱(chēng)。比如我們需要對(duì)如下格式的記錄中的 color 字段進(jìn)行索引時(shí),可以用 style.color 表示。在設(shè)置單字段索引時(shí),指定排序?yàn)樯蚧蚪敌蚨伎梢?,?/p>
{
"_id": '',
"style": {
"color": ''
}
}
組合索引即一個(gè)索引包含多個(gè)字段,組合索引在添加時(shí)要注意字段的順序,順序不同索引的效果也會(huì)不同。當(dāng)查詢條件使用的字段包含在索引定義的所有字段或前綴字段里時(shí),會(huì)命中索引。
組合索引遵循最左前綴原則,比如在 A, B, C 三個(gè)字段定義的組合索引(A, B, C),那么查詢條件A,{A, B},{A, C},{A, B, C}索引都會(huì)有效,查詢條件B,C,{B, C}則不會(huì)命中索引。根據(jù)最左前綴原則,我們可以明白組合索引(A, B)和調(diào)換字段順序的(B, A)效果是不一樣的。當(dāng)定義組合索引為(A, B)時(shí),索引會(huì)先按 A 字段排序再按 B 字段排序。因此當(dāng)組合索引設(shè)為 (A, B) 時(shí),即使我們沒(méi)有單獨(dú)對(duì)字段 A 設(shè)立索引,但對(duì)字段 A 的查詢可以命中 (A, B) 索引。
定義索引時(shí)字段的排序方式也決定排序查詢是否有效,比如我們對(duì)字段 A 和 B 設(shè)置以下索引:(A: 升序,B: 降序),那么當(dāng)我們查詢需要對(duì) A, B 進(jìn)行排序時(shí),可以指定排序結(jié)果為 A 升序、B 降序以及完全相反的排序A 降序、B 升序有效,而A升B升,A降B降都不會(huì)命中索引。
還有一些查詢條件,需要進(jìn)行范圍查詢或者排序,那么范圍查詢和排序的字段就要盡量往后放,因?yàn)榉秶樵円院蟮淖侄嗡饕遣荒苊械摹=M合索引的好處已經(jīng)在上面有提到了,如果數(shù)據(jù)庫(kù)有a索引,現(xiàn)在b列也需要索引,那么直接建立(a,b)即可
創(chuàng)建索引時(shí)可以指定增加唯一性限制,具有唯一性限制的索引會(huì)要求被索引集合不能存在被索引字段值都相同的兩個(gè)記錄。需特別注意的是,假如記錄中不存在某個(gè)字段,則對(duì)索引字段來(lái)說(shuō)其值默認(rèn)為 null,如果索引有唯一性限制,則不允許存在兩個(gè)或以上的該字段為空 / 不存在該字段的記錄。
db.collection("china")
.where({
gdp: _.gt(3000),
city_area:_.lt(10000),
reg_pop:_.gt(6000)
})
.field({
_id:false,
city: true,
city_area: true,
gdp:true
})
.orderBy('gdp', 'desc')
.orderBy('city_area', 'asc')
由于有三個(gè)查詢條件,為你可以給三個(gè)查詢條件按照順序創(chuàng)建索引,由于這幾個(gè)值無(wú)法做到非唯一,且存在空值的可能(有些城市沒(méi)有數(shù)據(jù)),所以創(chuàng)建時(shí)選擇非唯一。
索引雖然能非常高效的提高查詢速度,同時(shí)卻會(huì)降低更新表的速度。實(shí)際上索引也是一張表,該表保存了主鍵與索引字段,并指向?qū)嶓w表的記錄,所以索引列也是要占用空間的。索引需要進(jìn)行兩次查找,一次是查找索引條目,一次是根據(jù)索引指針去查找相應(yīng)的文檔,而全表查詢只需要進(jìn)行一次查找。集合較大、文檔較大、選擇性查詢就比較適合用索引。
其實(shí)建索引的原理就是將磁盤(pán)I/O操作的最小化,不在磁盤(pán)中排序,而是在內(nèi)存中排好序,通過(guò)排序的規(guī)則去指定磁盤(pán)讀取就行,也不需要在磁盤(pán)上隨機(jī)讀取。
索引并非越多越好,一個(gè)表中如有大量的索引,不僅占用磁盤(pán)空間,而且會(huì)影響增刪改等語(yǔ)句的性能,因?yàn)楫?dāng)表中的數(shù)據(jù)更改的同時(shí),索引也會(huì)進(jìn)行調(diào)整和更新。避免對(duì)經(jīng)常更新的表設(shè)計(jì)過(guò)多的索引,并且索引中的列盡可能要少,而對(duì)經(jīng)常用于查詢的字段應(yīng)該創(chuàng)建索引,但要避免添加不必要的字段。
為了減少索引的數(shù)量,可以建立組合索引,組合索引就是可以使用多個(gè)列一起建立一個(gè)索引。建立索引時(shí)要優(yōu)先在已經(jīng)存在的索引上擴(kuò)展成組合索引,或者在已經(jīng)存在的組合索引上繼續(xù)添加字段。因?yàn)?,索引越多,維護(hù)成本就越高,還會(huì)導(dǎo)致插入速度變慢等負(fù)面效應(yīng)。
哪些情況需要?jiǎng)?chuàng)建索引:
哪些情況不需要?jiǎng)?chuàng)建索引:
在云開(kāi)發(fā)控制臺(tái)每個(gè)集合都有相應(yīng)的索引管理,在這里除了可以創(chuàng)建索引外,還可以了解每個(gè)索引占據(jù)的空間以及判斷查詢時(shí)索引是否命中的命中數(shù)。每個(gè)索引建議創(chuàng)建的索引數(shù)不要超過(guò)5個(gè),索引占據(jù)的空間
1、最好是使用唯一索引
當(dāng)唯一性是某種數(shù)據(jù)本身的特征時(shí),指定唯一索引。使用唯一索引需能確保定義的列的數(shù)據(jù)完整性,以提高查詢速度
2、和簡(jiǎn)單的字段為索引
Innodb 表的普通索引都會(huì)保存主鍵的鍵值,所以主鍵要盡可能選擇較短的數(shù)據(jù)類(lèi)型,可以有效的減少索引的磁盤(pán)占用,提高索引的緩存效果。索引的太長(zhǎng)首先會(huì)占用大量的磁盤(pán)空間,其次索引太長(zhǎng)會(huì)使索引變得臃腫,導(dǎo)致索引查詢變慢。通過(guò)目錄查詢書(shū)籍指定的章節(jié)之所以快,就是因?yàn)樗饕銐蜉p量,如果索引太長(zhǎng)那么這個(gè)優(yōu)勢(shì)就不明顯了。而且索引里的數(shù)據(jù)和表里的數(shù)據(jù)本身就是冗余的,如果索引太長(zhǎng),那么磁盤(pán)空間浪費(fèi)的就越多。
3、用區(qū)分度比較高的列建索引
具有多個(gè)重復(fù)值的字段,其索引效果最差。比如存放身份證的字段因?yàn)橹刀疾煌?,很容易區(qū)分,索引效果比較好,而用來(lái)記錄性別的字段,因?yàn)橹缓小澳小?,“女”,不管搜索哪個(gè)值,都會(huì)得出大約一半的值,這樣的索引對(duì)性能的提升不高。如果有幾個(gè)列都是唯一的,要選擇最常作為訪問(wèn)條件的列作為索引的主鍵。簡(jiǎn)單枚舉值的列不要建立索引。在條件表達(dá)式中經(jīng)常用到的不同值較多的列上建立索引,在不同值較少的列上不要建立索引,比如性別字段只有男和女,就沒(méi)必要建立索引。如果建立索引不但不會(huì)提高查詢效率,反而會(huì)嚴(yán)重降低更新速度。由于我們建立索引并想讓索引能達(dá)到最高性能,這個(gè)時(shí)候我們應(yīng)當(dāng)充分考慮該列是否適合建立索引,可以根據(jù)列的區(qū)分度來(lái)判斷,區(qū)分度太低的情況下可以不考慮建立索引,區(qū)分度越高效率越高。
4、索引的字段的值最好不要有空值
索引的字段最好不要有空值,有空值的字段建立索引時(shí)要選擇非唯一性。唯一性的索引是不允許有空值的。
5、索引的字段最好不要參與計(jì)算
索引列不能參與計(jì)算,保持列“干凈”;索引列參與計(jì)算;應(yīng)盡量避免在 where 子句中對(duì)字段進(jìn)行表達(dá)式操作
6、查詢盡量都走在索引上
保證索引包含的字段獨(dú)立在查詢語(yǔ)句中,不能是在表達(dá)式中,當(dāng)查詢的列都在索引的字段中時(shí),查詢的效率更高,所以應(yīng)該盡量避免使用 select *,需要哪些字段,就只查哪些字段。在索引基數(shù)高的地方建立索引(比如郵箱,用戶名,而不是性別)
7、避免重復(fù)索引和冗余索引
重復(fù)索引:在同一列或者相同順序的幾個(gè)列建立了多個(gè)索引,成為重復(fù)索引,沒(méi)有任何意義,刪掉 冗余索引:兩個(gè)或多個(gè)索引所覆蓋的列有重疊,比如對(duì)于列m,n ,加索引index m(m),indexmn(m,n),稱(chēng)為冗余索引。
8、使用索引獲取有序數(shù)據(jù)
索引本身是有序的,利用有序索引獲取有序數(shù)據(jù)(Using Index)。使用索引來(lái)優(yōu)化或者避免排序排序的字段上加入索引,可以提高速度。在頻繁排序或分組(即group by或order by操作)的列上建立索引,如果待排序的列有多個(gè),可以在這些列上建立組合索引
9、組合索引遵循最左前綴原則
建立組合索引,要同時(shí)考慮列查詢的頻率和列的區(qū)分度,區(qū)分度大的優(yōu)先放在前面。比如一張全球人口的用戶表,該表有性別,國(guó)籍,年齡等字段。那么一般情況下國(guó)籍的區(qū)分度就要比性別的區(qū)分度更高,比如滿足中國(guó)人這個(gè)條件的要比滿足男人這個(gè)條件的人要更少。因此建組合索引時(shí)國(guó)籍優(yōu)先考慮放在性別的前面。
10、索引碎片與維護(hù)
在數(shù)據(jù)表長(zhǎng)期的更改過(guò)程中,索引文件和數(shù)據(jù)文件都會(huì)產(chǎn)生空洞,形成碎片。修復(fù)表的過(guò)程十分耗費(fèi)資源,可以用比較長(zhǎng)的周期修復(fù)表。
11、注意索引的唯一性和非唯一性
唯一索引一定要小心使用,它帶有唯一約束,由于前期需求不明等情況下,可能造成我們對(duì)于唯一列的誤判。
12、建議最好不要用隨機(jī)生成的_id做主鍵
最好是使用自增ID字段做索引的主鍵,而不要使用隨機(jī)的_id做主鍵,因?yàn)榉沁f增的主鍵會(huì)導(dǎo)致頻繁的頁(yè)分裂,從而降低了插入的效率。所以一般情況下,我們會(huì)在表中使用一個(gè)自增ID字段來(lái)代替_id(使用原子更新指令inc來(lái)做自增),用該字段來(lái)作表的主鍵。如果需要按照_id查詢時(shí),查找就需要回表,查找的效率會(huì)低一點(diǎn)。如果表中只需要一個(gè)_id的唯一索引,那么就可以使用_id來(lái)做主鍵;如果不滿足這個(gè)條件就用自增ID做索引;
更多建議: