Mongo索引是基於B-tree,存儲在一個易於遍歷讀取的數據集合中,它是對數據庫表中一列或多列的值進行排序的一種結構。mongodb
數據庫的索引和咱們書籍目錄類似,有了索引,咱們不須要翻閱整本書,只須要查看目錄就知道咱們要的內容在哪兒,而且直接定位到,這種方式能大大提升咱們的查找效率。數據庫
爲了讓你們更直觀瞭解,我基於mongo3.6簡單插入了1百萬條數據進去,經過explain來進行分析查詢狀況。數組
scanv_rs:PRIMARY> db.users.count()
1000000
scanv_rs:PRIMARY> db.users.ensureIndex({"username": 1})
建立索引以後
scanv_rs:PRIMARY> db.users.find({"username": 'user10001'}).explain()
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.users",
"indexFilterSet" : false,
"parsedQuery" : {
"username" : {
"$eq" : "user10001"
}
},
"winningPlan" : {
"stage" : "FETCH", # 經過返回index位置檢索文檔
"inputStage" : {
"stage" : "IXSCAN", # 索引查找,沒有創建索引就是COLLSCAN
"keyPattern" : {
"username" : 1
},
"indexName" : "username_1", # 索引名字
"isMultiKey" : false, # 創建在數組上,這兒是true
"multiKeyPaths" : {
"username" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"username" : [
"[\"user10001\", \"user10001\"]"
]
}
}
},
"rejectedPlans" : [ ]
},
}
}
有部分刪減
scanv_rs:PRIMARY>db.users.find({"username":'user10001'}).explain('executionStats')
複製代碼
這裏就不進一步展現了。 上面最後語句執行的結果這裏就不展現了,結果就是在1000000條數據,"executionTimeMillis" 字段值創建前花費1450ms,創建後花費2ms, 相差百倍,totalDocsExamined 一個創建索引前全文掃描1000000條,創建後只有1條。安全
你們有興趣能夠自行對比一下。從上面咱們能夠看到索引的威力。bash
簡單說完索引以後,咱們再來聊下索引的分類,索引主要分爲:惟一索引和稀疏索引。服務器
惟一索引能夠確保集合的每個文檔的指定健都有惟一值。性能
舉個例子,咱們要在集合裏面創建username索引,經過這種方式能夠確保username在不一樣的文檔裏面擁有的username是惟一的(其實咱們經常使用_id索引也是惟一索引)測試
db.yourcollection.ensureIndex({"username":1}, {"unique": true})
spa
若是在上面的集合中添加相同username數據就會致使失敗 E11000 dumplicate key error…設計
咱們常常在集合上建立索引的時候會碰到上面那個錯誤,緣由就是咱們集合裏面已經有了重複的數據。
碰到這種狀況,一般的方式是
先找出重複的數據進行清理掉,再重建(線上),好比經過聚合
使用dropDups簡單粗暴處理
經過dropDups的方式,在建立索引的時候加上,能夠強制性創建惟一索引,遇到重複的值,第一個保留,其餘進行刪掉。
db.yourcollection.ensureIndex({"username":1}, {"unique": true, "dropDups": true})
第二種方式一般用在開發測試環境中,線上環境請注意。
說完惟一索引,咱們再來了解下稀疏索引。
因爲惟一索引會把null看作值,因此沒法將多個缺乏惟一索引中的健的文檔插入到集合中。
這個時候咱們能夠經過建立稀疏索引的方式來進行,一個值可存在可不存在,若是存在就必須是惟一的。咱們只須要添加一個spare選項就能建立稀疏索引。
好比咱們要創建一個可選的姓名,若是提供了姓名,那麼它的值必須是惟一的。
db.yourcollection.ensureIndex({'username': 1}, {'unique': true, 'sparse': true})
上面是單一健索引,其實咱們還有基於多個健的複合索引,全文索引,地理空間索引,因爲篇幅有限,這裏面咱們就先不深刻進去。
介紹分類以後,咱們聊聊怎麼創建索引,新建索引是一件費時費資源的事情,默認狀況索引建立會阻塞對數據庫的讀寫請求,一直到索引建立完成。
若是但願建立因此任然能處理讀寫請求,建立時咱們須要指定background參數。
好比在單機服務器上咱們能夠加上background 爲True。
db.yourcollection.ensureIndex({'username': 1}, {background: true})
這種方式雖然會消耗比較長的時間,可是不會鎖定數據庫,從而保證其餘操做的運行。
一樣在數據量小的集合的副本級上面咱們也能這樣作,在主節點上創建索引,而後同步到備份節點上面。
可是在數據量大的集合咱們須要拆分每一個節點來進行創建索引,避免索引期間全部副本級沒法正常工做,致使出現問題。
拆分從節點創建索引步驟以下:
關閉一個從節點A,獨立啓動
在這個從節點A創建索引
從新將A加入副本級
重複上面三個步奏
對於主節點咱們能夠進行故障轉移爲從節點或者直接進行創建索引(對性能有必定影響),經過上面的方式就能大大提升咱們創建索引安全穩定性。
我曾經就碰到過有同窗沒有拆分執行就創建索引的狀況,致使幾臺DB節點打滿,沒法工做,你們須要注意下,若是因爲環境因素作不到,那麼咱們須要找DB空閒時間進行上述操做。
雖然絕大多數場景,咱們都必需要有索引才能提升效率。
但有時候咱們須要考慮是否真的有必要使用索引,由於使用索引須要進行兩次查找,一次查找索引條目,一次根據索引指針查找相應的文檔,而全表掃描只須要一次查找過程。
下面咱們來對比一下,索引適用與不適用狀況。
從上面圖咱們知道索引適合,集合大,文檔大,選擇性查詢狀況,不適合與之相反的集合小,文檔小,非選擇性查詢的狀況。
關於索引的一些建議:
學會使用explain進行分析,對比索引和非索引區別,檢索條數,消耗毫秒數等
關注讀寫比率,由於若是應用寫多讀少 ,添加索引會影響寫入性能
在索引基數高的地方創建索引(好比郵箱,用戶名,而不是性別)
$or 查詢是兩次獨立查詢拼接而成,效率沒有使用 $IN的高
$ne 或者 $nin 操做在索引上是無效的
設計多個字段索引時,先用精確匹配查詢,而後再用範圍匹配(好比y>10&&y<100)的字段