MongoDB的索引和MySql的索引的作用和优化要遵循的准则根本类似,MySql索引类型根本能够辨别为:

  • 单键索引 - 联结索引
  • 主键索引(聚簇索引) - 非主键索引(非聚簇索引)

MongoDB中除了这些根底的分类之外,还有一些非凡的索引类型,如: 数组索引 | 稠密索引 | 天文空间索引 | TTL索引等.

为了上面不便测试咱们应用脚本插入以下数据

for(var i = 0;i < 100000;i++){    db.users.insertOne({        username: "user"+i,        age: Math.random() * 100,        sex: i % 2,        phone: 18468150001+i    });}

单键索引

单键索引即索引的字段只有一个,是最根底的索引形式.

在汇合中应用username字段,创立一个单键索引,MongoDB会主动将这个索引命名为username_1

db.users.createIndex({username:1})'username_1'

在创立索引后查看一下应用username字段的查问打算,stageIXSCAN代表应用应用了索引扫描

db.users.find({username:"user40001"}).explain(){    queryPlanner:    {      winningPlan:      {         ......        stage: 'FETCH',        inputStage:         {            stage: 'IXSCAN',           keyPattern: { username: 1 },           indexName: 'username_1',           ......        }      }     rejectedPlans: [] ,   },   ......   ok: 1 }

在索引优化的准则当中,有很重要的准则就是索引要建设在基数高的的字段上,所谓基数就是一个字段上不反复数值的个数,即咱们在创立users汇合时年龄呈现的数值是0-99那么age这个字段将会有100个不反复的数值,即age字段的基数为100,而sex这个字段只会呈现0 | 1这个两个值,即sex字段的根底是2,这是一个相当低的基数,在这种状况下,索引的效率并不高并且会导致索引生效.

上面就船舰一个sex字段索引,来查问执行打算会发现,查问时是走的全表扫描,而没有走相干索引.

db.users.createIndex({sex:1})'sex_1'db.users.find({sex:1}).explain(){   queryPlanner:   {      ......     winningPlan:      {         stage: 'COLLSCAN',        filter: { sex: { '$eq': 1 } },        direction: 'forward'      },     rejectedPlans: []   },  ......  ok: 1 }

联结索引

联结索引即索引上会有多个字段,上面应用agesex两个字段创立一个索引

db.users.createIndex({age:1,sex:1})'age_1_sex_1'

而后咱们应用这两个字段进行一次查问,查看执行打算,顺利地走了这条索引

db.users.find({age:23,sex:1}).explain(){   queryPlanner:   {      ......     winningPlan:      {         stage: 'FETCH',        inputStage:         {            stage: 'IXSCAN',           keyPattern: { age: 1, sex: 1 },           indexName: 'age_1_sex_1',           .......           indexBounds: { age: [ '[23, 23]' ], sex: [ '[1, 1]' ] }         }      },     rejectedPlans: [],   },  ......  ok: 1  }

数组索引

数组索引就是对数组字段创立索引,也叫做多值索引,上面为了测试将users汇合中的数据减少一部分数组字段.

db.users.updateOne({username:"user1"},{$set:{hobby:["唱歌","篮球","rap"]}})......

创立数组索引并进行查看其执行打算,留神isMultiKey: true示意应用的索引是多值索引.

db.users.createIndex({hobby:1})'hobby_1'db.users.find({hobby:{$elemMatch:{$eq:"钓鱼"}}}).explain(){    queryPlanner:    {      ......     winningPlan:      {         stage: 'FETCH',        filter: { hobby: { '$elemMatch': { '$eq': '钓鱼' } } },        inputStage:         {            stage: 'IXSCAN',           keyPattern: { hobby: 1 },           indexName: 'hobby_1',           isMultiKey: true,           multiKeyPaths: { hobby: [ 'hobby' ] },           ......           indexBounds: { hobby: [ '["钓鱼", "钓鱼"]' ] } }          },     rejectedPlans: []   },  ......  ok: 1 }

数组索引相比于其它索引来说索引条目和体积必然呈倍数减少,例如均匀每个文档的hobby数组的size为10,那么这个汇合的hobby数组索引的条目数量将是一般索引的10倍.

联结数组索引

联结数组索引就是含有数组字段的联结索引,这种索引不反对一个索引中含有多个数组字段,即一个索引中最多能有一个数组字段,这是为了防止索引条目爆炸式增长,假如一个索引中有两个数组字段,那么这个索引条目标数量将是一般索引的n*m倍

天文空间索引

在原先的users汇合上,减少一些地理信息

for(var i = 0;i < 100000;i++){    db.users.updateOne(    {username:"user"+i},    {        $set:{            location:{                type: "Point",                coordinates: [100+Math.random() * 4,40+Math.random() * 3]            }        }    });}

创立一个二维空间索引

db.users.createIndex({location:"2dsphere"})'location_2dsphere'//查问500米内的人db.users.find({  location:{    $near:{      $geometry:{type:"Point",coordinates:[102,41.5]},      $maxDistance:500    }  }})

天文空间索引的type有很多蕴含Ponit(点) | LineString(线) | Polygon(多边形)

TTL索引

TTL的全拼是time to live,次要是用于过期数据主动删除,应用这种索引须要在文档中申明一个工夫类型的字段,而后为这个字段创立TTL索引的时候还须要设置一个expireAfterSeconds过期工夫单位为秒,创立实现后MongoDB会定期对汇合中的数据进行查看,当呈现:

$$以后工夫 - TTL索引字段时间 > expireAfterSrconds$$

MongoDB将会主动将这些文档删除,这种索引还有以下这些要求:

  • TTL索引只能有一个字段,没有联结TTL索引
  • TTL不能用于固定汇合
  • TTL索引是一一遍历后,发现满足删除条件会应用delete函数删除,效率并不高

首先在咱们文档上增减一个工夫字段

for(var i = 90000;i < 100000;i++){    db.users.updateOne(    {username:"user"+i},    {        $set:{            createdDate:new Date()        }    });}

创立一个TTL索引并且设定过期工夫为60s,待过60s后查问,会发现这些数据曾经不存在

db.users.createIndex({createdDate:1},{expireAfterSeconds:60})'createdDate_1'

另外还能够用CollMod命令更改TTL索引的过期工夫

db.runCommand({  collMod:"users",  index:{    keyPattern:{createdDate:1},    expireAfterSeconds:120  }}){ expireAfterSeconds_old: 60, expireAfterSeconds_new: 120, ok: 1 }

条件索引

条件索引也叫局部索引(partial),只对满足条件的数据进行建设索引.

只对50岁以上的user进行建设username_1索引,查看执行打算会发现isPartial这个字段会变成true

db.users.createIndex({username:1},{partialFilterExpression:{    age:{$gt:50}  }})'username_1'db.users.find({$and:[{username:"user4"},{age:60}]}).explain(){   queryPlanner:   {      ......     winningPlan:      {         stage: 'FETCH',        filter: { age: { '$eq': 60 } },        inputStage:         {            stage: 'IXSCAN',           keyPattern: { username: 1 },           indexName: 'username_1',           ......           isPartial: true,           ......         }      },     rejectedPlans: []   },  ......  ok: 1 }

稠密索引

个别的索引会依据某个字段为整个汇合创立一个索引,即便某个文档不存这个字段,那么这个索引会把这个文档的这个字段当作null建设在索引当中.

稠密索引不会对文档中不存在的字段建设索引,如果这个字段存在然而为null时,则会创立索引.

上面给users汇合中的局部数据创立稠密索引

for(var i = 5000;i < 10000;i++){  if(i < 9000){    db.users.updateOne(      {username:"user"+i},      { $set:{email:(120000000+i)+"@qq.email"}}    )  }else{    db.users.updateOne(      {username:"user"+i},      { $set:{email:null}}    )  }}

当不建设索引应用{email:null}条件进行查问时,咱们会发现查出来的文档蕴含没有email字段的文档

db.users.find({email:null}){   _id: ObjectId("61bdc01ba59136670f6536fd"),  username: 'user0',  age: 64.41483801726282,  sex: 0,  phone: 18468150001,  location:   {     type: 'Point',    coordinates: [ 101.42490900320335, 42.2576650823515 ]   } }......

而后对email这个字段创立一个稠密索引应用{email:null}条件进行查问,则发现查问来的文档全副是email字段存在且为null的文档.

db.users.createIndex({email:1},{sparse:true});'email_1'db.users.find({email:null}).hint({email:1}){   _id: ObjectId("61bdc12ca59136670f655a25"),  username: 'user9000',  age: 94.18397576757012,  sex: 0,  phone: 18468159001,  hobby: [ '钓鱼', '乒乓球' ],  location:   {     type: 'Point',    coordinates: [ 101.25903151863596, 41.38450145025062 ]   },  email: null }......

文本索引

文本索引将建设索引的文档字段先进行分词再进行检索,然而目前还不反对中文分词.

上面减少两个文本字段,创立一个联结文本索引

db.blog.insertMany([  {title:"hello world",content:"mongodb is the best database"},  {title:"index",content:"efficient data structure"}])//创立索引db.blog.createIndex({title:"text",content:"text"})'title_text_content_text'//应用文本索引查问db.blog.find({$text:{$search:"hello data"}}){   _id: ObjectId("61c092268c4037d17827d977"),  title: 'index',  content: 'efficient data structure' },{   _id: ObjectId("61c092268c4037d17827d976"),  title: 'hello world',  content: 'mongodb is the best database' }

惟一索引

惟一索引就是在建设索引地字段上不能呈现反复元素,除了单字段惟一索引还有联结惟一索引以及数组惟一索引(即数组之间不能有元素交加 )

//对title字段创立惟一索引db.blog.createIndex({title:1},{unique:true})'title_1'//插入一个曾经存在的title值db.blog.insertOne({title:"hello world",content:"mongodb is the best database"})MongoServerError: E11000 duplicate key error collection: mock.blog index: title_1 dup key: { : "hello world" }//查看一下执行打算,isUnique为truedb.blog.find({"title":"index"}).explain(){   queryPlanner:   {      ......     winningPlan:      {         stage: 'FETCH',        inputStage:         {            stage: 'IXSCAN',           keyPattern: { title: 1 },           indexName: 'title_1',           isMultiKey: false,           multiKeyPaths: { title: [] },           isUnique: true,           ......         }      },     rejectedPlans: []   },  .......  ok: 1 }