MongoDB索引與優化詳解

五月君發表於2019-04-27

在MongoDB中通過建立索引可以進行高效的查詢,如果沒有索引MongoDB將會掃描整個集合與查詢的條件進行匹配,這對於效能會造成很大的消耗。

技術部落格: Node.js技術棧

快速導航

面試指南

  • 生產環境如何正確建立索引?,參考:#

Mongodb索引型別

MongoDB提供了不同的索引型別支援在不同的業務場景進行查詢

1. _id索引

絕大多數集合預設建立索引,對於每個插入的資料,MongoDB都會生成一條唯一的_id欄位。

例如新建立一個集合時

db.demo_admin2.insert({x:1})
db.demo_admin2.getIndexes() # 檢視集合索引,可看到_id索引
複製程式碼

2. 單鍵索引

是最普通的索引,單鍵索引不會自動建立

例如一條記錄形式為:{x:1,y:2,z:3},只要在x欄位上建立索引之後,就可以用x為條件進行查詢

db.demo_admin2.ensureIndex({x:1}) # 建立索引
db.demo_admin2.find({x:1}); # 使用索引查詢
複製程式碼

3. 多鍵索引

多鍵索引與單鍵索引區別在於多鍵索引的值具有多個記錄,是一個陣列

db.demo_admin2.insert({x:[1,2,3,4]})
複製程式碼

4. 複合索引

當查詢條件為多個時,需要建立複合索引

插入記錄{'x':1,'y':2,'z':3}

db.demo_admin2.insert({'x':1,'y':2,'z':3});
複製程式碼

建立索引

db.demo_3.ensureIndex({x:1,y:1}) # 1升序,-1降序
複製程式碼

使用{x:1,y:1}作為條件進行查詢

db.demo_admin2.find({x:1,y:2})
複製程式碼

5. 過期索引

指在一段時間後會過期的索引,此索引過一段時間會過期,索引過期後,相應的資料會被刪除,適合儲存一些在一段時間之後會失效的資料,比如使用者登入資訊,這樣就不需要用到session了。

5.1 建立過期索引

建立索引的時候需要多用一個引數,指定索引的有效時間——expireAfterSeconds,單位為秒

如下示例為time欄位建立過期索引

db.demo_3.ensureIndex({time:1},{expireAfterSeconds:10})
複製程式碼

5.2 過期索引限制

  • 過期索引欄位值必須是指定的時間型別,必須是ISODate或ISODate陣列,不能使用時間戳,否則不能被自動刪除。

  • 如果指定了ISODate陣列,則按照最小的時間進行刪除。 過期索引不能是複合索引。

  • 刪除時間不是精確的,刪除過程是由後臺程式每60s跑一次,而且刪除也需要一些時間,所以存在誤差。

db.demo_3.ensureIndex({time:1},{expireAfterSeconds:30}
db.demo_3.insert({time:new Date()});
複製程式碼

6. 全文索引

在mongodb中每個集合只允許建立一個索引,因此不用擔心存在多個索引造成衝突的問題。

6.1 全文索引建立

全文索引建立方法與建立單鍵索引、複合索引類似。value換成'text',$**匹配集合下所有

為一個欄位建立全文索引

db.articles.ensureIndex({key:"text"})
複製程式碼

為多個欄位建立全文索引

db.articles.ensureIndex({key_1:"text"},{key_2:"text"})
複製程式碼

為集合中所有的欄位建立全文索引

db.articles.ensureIndex({"$**":"text"})
複製程式碼

6.2 例項

建立索引

db.article.ensureIndex({"article":"text"})
db.articles.find({$text:{$search:"coffee"}})
db.articles.find({$text:{$search:"aa bb cc"}}) # 包含aa或bb或cc的資料
db.articles.find({$text:{$search:"aa bb -cc"}}) # 同時包含aa、bb且不包含cc的資料
db.articles.find({$text:{$search:"\"aa\" \"bb\" \"cc\""}})# 同時包含aa、bb、cc的資料(用""包裹起來,引號需要用反斜槓\轉義)
複製程式碼

6.3 mongodb相似度查詢

$meta操作符:{score:{$meta:'textScore'}}

查詢結果的相似度,搜尋出的結果會多出一個score欄位,這個得分越高,相關度越高。

db.article.find({$text:{$search:"aa bb ff"}},{score:{$meta:"textScore"}})
複製程式碼

加上.sort方法可排序

db.article.find({$text:{$search:"aa bb ff"}},{score:{$meta:"textScore"}}).sort({score:{$meta:"textScore"}})
複製程式碼

6.4 全域性索引的限制

  1. 每次查詢,只能指定一個$text查詢
  2. $text查詢不能出現在$nor查詢中
  3. 查詢中如果包含了$text, hint不再起作用
  4. MongoDB全文索引還不支援中文

7.地理位置索引

  • 2d索引,用於儲存和查詢平面上的點
  • 2dsphere索引,用於儲存和查詢球面上的點

索引屬性

1.索引屬性name

MongoDB會自動的建立,規則是key_1 或者 key_-1 1或者-1代表排序方向,一般影響不大,長度一般有限制125位元組

db.collection.ensureIndex({indexValue},{name: key})
複製程式碼

為了見名知意我們可以自己來命名

db.demo_3.ensureIndex({x:1,y:1,z:1,n:1},{name:'xyz-name'});
複製程式碼

刪除索引

db.demo_3.dropIndex(indexName)
複製程式碼

2. 索引屬性unique唯一性指定

類似關係型資料庫欄位的唯一約束

db.demo_3.ensureIndex({m:1,n:1},{unique:true})
複製程式碼

索引例項測試

建立500萬條資料,分別用來測試建立索引和未建立索引的差別,只有在大量資料下才有效果,以下的示例中的時間消耗值,各電腦配置的不同在不同電腦上測試也會有不同的差別。

  • 建立測試資料
> for(var i=0; i<5000000; i++) db.demo_user.insert({id: i})
WriteResult({ "nInserted" : 1 })
複製程式碼
  • 未建立索引情況資料查詢

在未建立索引的情況下,執行資料查詢的時間消耗在6秒多。

圖片描述

  • 建立索引情況查詢資料
db.getCollection('demo_user').ensureIndex({"id": 1}) # 建立索引
複製程式碼

下圖為建立索引的情況,在資料查詢中僅用了0.001秒,可見建立索引的重要的性。

圖片描述

索引導致的庫級鎖

這是一個很坑爹的事情,MongoDB沒有像MySql、Oracle擁有行級粒度鎖概念,在MongoDB中只有庫級粒度鎖概念,意味這當你在生產環境中不小心觸發了一個寫鎖的操作時其它的業務也會受影響。

MongoDB中建立索引就是一個觸發寫鎖的過程,通常資料量越大建立的索引佔用的寫鎖時間就會越長,MongoDB中建立索引的兩種方式。

  • 前臺建立(錯誤)

以下為前臺建立索引演示可以看到在執行建立索引命令之後,新開啟一個終端查詢另一張表一直處於等待狀態,直到建立索引完成才返回資料。

db.getCollection('demo_user').ensureIndex({"id": 1}) # 建立索引
複製程式碼

圖片描述

  • 後臺建立(推薦) 基於後臺建立索引的方式在執行建立索引命令之後,新開一個終端查詢另一個集合中的資料可以立即得到返回。
db.getCollection('demo_user').ensureIndex({"id": 1}, {background: 1}) # 建立索引
複製程式碼

圖片描述

作者:五月君
連結:www.imooc.com/article/285…
來源:慕課網
Github: Node.js技術棧

推薦閱讀

相關文章