天天看點

快速掌握mongoDB(三)——mongoDB的索引詳解

1 mongoDB索引的管理

  本節介紹mongoDB中的索引,熟悉mysql/sqlserver等關系型資料庫的小夥伴應該都知道索引對優化資料查詢的重要性。我們先簡單了解一下索引:索引的本質就是一個排序的清單,在這個清單中存儲着索引的值和包含這個值的資料(資料row或者document)的實體位址,索引可以大大加快查詢的速度,這是因為使用索引後可以不再掃描全表來定位某行的資料,而是先通過索引表找到該行資料對應的實體位址(多數為B-tree查找),然後通過位址來通路相應的資料。

  索引可以加快資料檢索、排序、分組的速度,減少磁盤I/O,但是索引也不是越多越好,因為索引本身也是資料表,需要占用存儲空間,同時索引需要資料庫進行維護,當我們對索引列的值進行增改删操作時,資料庫需要更新索引表,這會增加資料庫的壓力。

我們要根據實際情況來判斷哪些列适合添加索引,哪些列不适合添加索引,一般遵循的規律如下:

  主/外鍵列,主鍵用于強制該列的唯一性群組織表中資料的排列結構;外鍵可以加快連接配接的速度;

  經常用于比較的類(大于小于等于等),因為索引已經排序,值就是大于/小于的分界點;

  經常進行範圍搜尋,因為索引已經排序,其指定的範圍是連續的;

  經常進行排序的列,因為索引已經排序,這樣查詢可以利用索引的排序,加快排序查詢時間;

  經常進行分組的列,因為索引已經排序,同一個值的所有資料位址會聚集在一塊,很友善分組。

我們看一下mongoDB的索引使用,首先準備資料:

db.userinfos.insertMany([
   {_id:1, name: "張三", age: 23,level:10, ename: { firstname: "san", lastname: "zhang"}, roles: ["vip","gen" ]},
   {_id:2, name: "李四", age: 24,level:20, ename: { firstname: "si", lastname: "li"}, roles:[ "vip" ]},
   {_id:3, name: "王五", age: 25,level:30, ename: { firstname: "wu", lastname: "wang"}, roles: ["gen","vip" ]},
   {_id:4, name: "趙六", age: 26,level:40, ename: { firstname: "liu", lastname: "zhao"}, roles: ["gen"] },
   {_id:5, name: "田七", age: 27, ename: { firstname: "qi", lastname: "tian"}, address:'北京' },
   {_id:6, name: "周八", age: 28,roles:["gen"], address:'上海' }
]);        

  索引的增删改查還是十分簡單的,我們看一下索引管理的幾個方法:

//建立索引,值1表示正序排序,-1表示倒序排序
  db.userinfos.createIndex({age:-1})

//檢視userinfos中的所有索引
  db.userinfos.getIndexes()

//删除特定一個索引
  db.userinfos.dropIndex({name:1,age:-1})
//删除所有的索引(主鍵索引_id不會被删除)
  db.userinfos.dropIndexes()

//如果我們要修改一個索引的話,可以先删除索引然後在重新添加。       

2 mongoDB中常用的索引類型

1 單鍵索引

  單鍵索引(Single Field Indexes)顧名思義就是單個字段作為索引列,mongoDB的所有collection預設都有一個單鍵索引_id,我們也可以對一些經常作為過濾條件的字段設定索引,如給age字段添加一個索引,文法十分簡單:

//給age字段添加升序索引
  db.userinfos.createIndex({age:1})      

  其中{age:1}中的1表示升序,如果想設定倒序索引的話使用 db.userinfos.createIndex({age:-1}) 即可。我們通過explain()方法檢視查詢計劃,如下圖,看到查詢age=23的document時使用了索引,如果沒有使用索引的話stage=COLLSCAN。

快速掌握mongoDB(三)——mongoDB的索引詳解

  因為document的存儲是bson格式的,我們也可以給内置對象的字段添加索引,或者将整個内置對象作為一個索引,文法如下:

//1.内嵌對象的某一字段作為索引
//在ename.firstname字段上添加索引
  db.userinfos.createIndex({"ename.firstname":1})
//使用ename.firstname字段的索引查詢
  db.userinfos.find({"ename.firstname":"san"})

//2.整個内嵌對象作為索引
//給整個ename字段添加索引
  db.userinfos.dropIndexes()
//使用ename字段的索引查詢
  db.userinfos.createIndex({"ename":1})      

2 複合索引

  複合索引(Compound Indexes)指一個索引包含多個字段,用法和單鍵索引基本一緻。使用複合索引時要注意字段的順序,如下添加一個name和age的複合索引,name正序,age倒序,document首先按照name正序排序,然後name相同的document按age進行倒序排序。mongoDB中一個複合索引最多可以包含32個字段。

//添加複合索引,name正序,age倒序
    db.userinfos.createIndex({"name":1,"age":-1}) 
//過濾條件為name,或包含name的查詢會使用索引(索引的第一個字段)
    db.userinfos.find({name:'張三'}).explain()
    db.userinfos.find({name:"張三",level:10}).explain()
    db.userinfos.find({name:"張三",age:23}).explain()

//查詢條件為age時,不會使用上邊建立的索引,而是使用的全表掃描
db.userinfos.find({age:23}).explain()      

  執行查詢時查詢計劃如下:

快速掌握mongoDB(三)——mongoDB的索引詳解

3 多鍵索引

  多鍵索引(mutiKey Indexes)是建在數組上的索引,在mongoDB的document中,有些字段的值為數組,多鍵索引就是為了提高查詢這些數組的效率。看一個栗子:準備測試資料,classes集合中添加兩個班級,每個班級都有一個students數組,如下:

db.classes.insertMany([
     {
         "classname":"class1",
         "students":[{name:'jack',age:20},
                    {name:'tom',age:22},
                    {name:'lilei',age:25}]
      },
      {
         "classname":"class2",
         "students":[{name:'lucy',age:20},
                    {name:'jim',age:23},
                    {name:'jarry',age:26}]
      }]
  )      

  為了提高查詢students的效率,我們使用  db.classes.createIndex({'students.age':1}) 給students的age字段添加索引,然後使用索引,如下圖:

快速掌握mongoDB(三)——mongoDB的索引詳解

 4 哈希索引

  哈希索引(hashed Indexes)就是将field的值進行hash計算後作為索引,其強大之處在于實作O(1)查找,當然用哈希索引最主要的功能也就是實作定值查找,對于經常需要排序或查詢範圍查詢的集合不要使用哈希索引。

快速掌握mongoDB(三)——mongoDB的索引詳解

3 mongoDB中常用的索引屬性

1  唯一索引

  唯一索引(unique indexes)用于為collection添加唯一限制,即強制要求collection中的索引字段沒有重複值。添加唯一索引的文法:

//在userinfos的name字段添加唯一索引
db.userinfos.createIndex({name:1},{unique:true})      

  看一個使用唯一索引的栗子:

快速掌握mongoDB(三)——mongoDB的索引詳解

2  局部索引

  局部索引(Partial Indexes)顧名思義,隻對collection的一部分添加索引。建立索引的時候,根據過濾條件判斷是否對document添加索引,對于沒有添加索引的文檔查找時采用的全表掃描,對添加了索引的文檔查找時使用索引。使用方法也比較簡單:

//userinfos集合中age>25的部分添加age字段索引
    db.userinfos.createIndex(
        {age:1},
        { partialFilterExpression: {age:{$gt: 25 }}}
    )
//查詢age<25的document時,因為age<25的部分沒有索引,會全表掃描查找(stage:COLLSCAN)
    db.userinfos.find({age:23})
//查詢age>25的document時,因為age>25的部分建立了索引,會使用索引進行查找(stage:IXSCAN)
    db.userinfos.find({age:26})      

  當查詢age=23的記錄時,stage=COLLSCAN,當查詢age=26的記錄時,使用了索引,如下:

快速掌握mongoDB(三)——mongoDB的索引詳解

2 稀疏索引

  稀疏索引(sparse indexes)在有索引字段的document上添加索引,如在address字段上添加稀疏索引時,隻有document有address字段時才會添加索引。而普通索引則是為所有的document添加索引,使用普通索引時如果document沒有索引字段的話,設定索引字段的值為null。

  稀疏索引的建立方式如下,當document包含address字段時才會建立索引:

//建立在address上建立稀疏索引
  db.userinfos.createIndex({address:1},{sparse:true})      

  看一個使用稀疏索引的栗子:

快速掌握mongoDB(三)——mongoDB的索引詳解

4 TTL索引

  TTL索引(TTL indexes)是一種特殊的單鍵索引,用于設定document的過期時間,mongoDB會在document過期後将其删除,TTL非常容易實作類似緩存過期政策的功能。我們看一個使用TTL索引的栗子:

//添加測試資料
db.logs.insertMany([
       {_id:1,createtime:new Date(),msg:"log1"},
       {_id:2,createtime:new Date(),msg:"log2"},
       {_id:3,createtime:new Date(),msg:"log3"},
       {_id:4,createtime:new Date(),msg:"log4"}
       ])
       //在createtime字段添加TTL索引,過期時間是120s
       db.logs.createIndex({createtime:1}, { expireAfterSeconds: 120 })


//logs中的document在建立後的120s後過期,會被mongoDB自動删除      

  注意:TTL索引隻能設定在date類型字段(或者包含date類型的數組)上,過期時間為字段值+exprireAfterSeconds;document過期時不一定就會被立即删除,因為mongoDB執行删除任務的時間間隔是60s;capped Collection不能設定TTL索引,因為mongoDB不能主動删除capped Collection中的document。

小結

  本節介紹了mongoDB中常用的索引和索引屬性,索引對提升資料檢索的速度十分重要,在資料量比較大的時候一般都要在collection上建立索引。mongoDB提供的索引種類很豐富,總會有幾種适用于我們的業務,除了上邊介紹的索引外,mongoDB還支援text index和一些地理位置相關的索引,這裡不再介紹,有興趣的小夥伴可以到官網 研究下。如果文中有錯誤的話,希望大家可以指出,我會及時修改,謝謝。

作者:撈月亮的猴子

    出處:https://www.cnblogs.com/wyy1234/

    歡迎轉載,但請标明出處。如果本文對您有些許幫助,點選一下推薦吧,Thanks♪(・ω・)ノ