Skip to content

索引

  • 索引就相当于字典中的目录(拼音/偏旁部首手) 有了目录我们就能通过目录快速的找到想要的结果.
  • 但是如果没有目录(拼音/偏旁部首手), 没有索引 那么如果想要查找某条数据就必须从前往后一条一条的查找
  • 所以索引就是用于提升数据的查询速度的
  • MongoDB和MySQL一样,默认情况下会给主键自动创建索引

获取索引

db.person.getIndexes()

创建索引

db.<collection>.createIndex({<field>:<1 or -1>, ...}, <options>)

<keys> : 指定创建索引的字段

<options>: 索引的额外配置

查看当前查询操作有没有使用索引

db.person.explain().find({age:18})

db.<collection>.explain().<method()>

winningPlan->stage->COLLSCAN->遍历整个集合查询

winningPlan->stage->IXSCAN-> 通过索引查询

winningPlan->stage->FETCH-> 根据索引存储的地址取出对应文档

技巧:我们查询的结果仅仅是索引的内容,那么查询的效率会更高, 不会根据索引去取出对应文档的地址,会直接将索引返回回来

复合索引

和MySQL一样MongoDB也支持复合索引

db.person.createIndex({name:1}) //如果只指定了一个字段,那么就是单值索引 db.person.createIndex({name:1,age:-1}) // 如果创建索引的时候制定了多个字段,那么就是复合索引

使用复合索引

  • 如果创建的是复合索引,那么在使用的时候,只支持前缀查询

例如:复合索引是A,B,C

  1. 如果使用A,B,C来查询,就会使用索引来查询
  2. 如果使用A,B来查询,会使用索引
  3. 如果使用A来查询,会使用索引
  4. 使用B来查询,不会使用索引
  5. 使用B,C查询,不会使用索引
  6. 使用C查询,不会使用索引

db.person.explain().find({name:'bs',age:20})

多键索引

db.person.insert([
{name:'as', age:18, tags:['ahtml', 'bcss']},
{name:'bs', age:17, tags:['cjs', 'enode']},
{name:'cs', age:19, tags:[ 'dvue', 'freact']},
])

多键索引是专门针对数组字段的,会给每个数组字段创建一个索引

db.person.createIndex({tags:1})

创建完数组索引的存储在内存的样子:


ahtml:{name:'as', age:18, tags:['ahtml', 'bcss']}
bcss:{name:'as', age:18, tags:['ahtml', 'bcss']}
cjs:{name:'bs', age:17, tags:['cjs', 'enode']}
enode:{name:'bs', age:17, tags:['cjs', 'enode']}
...

索引对排序的影响

如果排序字段是索引字段,会大大提升排序的效率

db.person.insert([
{name:'cs', age:19},
{name:'as', age:18},
{name:'bs', age:17}
])

没有索引:在执行的时候再去排序,然后再输出

db.person.explain().find().sort({age:1})

存在索引:在执行的时候不再去排序,直接获取索引对应的值,直接输出

db.person.createIndex({age:1})

db.person.explain().find().sort({age:1})

注意点:

如果是复合索引,那么只有在排序的时候指定的字段和索引创建的时候一模一样才会使用索引

db.person.createIndex({name:1,age:-1})

  • 会使用索引的情况 db.person.explain().find().sort({name:1,age:-1})

db.person.explain().find().sort({name:1})

  • 不会使用索引 db.person.explain().find().sort({age:-1})

db.person.explain().find().sort({age:-1,name:1})

同之前创建索引一致,只有先使用前缀的时候索引才会生效

唯一索引

默认情况下MongoDB和MySQL一样,都会自动为主键创建索引 除了主键可以作为唯一索引以外, 只要某个字段的取值是唯一的, 我们也可以手动给这个字段添加唯一索引

格式: db.<collection>.createIndex({<field>:<1 or -1>, ...}, {unique:true}})

db.person.createIndex({age:1},{unique:true})

age字段添加了唯一索引,这个字段的取值就不能重复

如果给某个字段添加了唯一索引,那么如果在新增其他数据的时候,没有唯一索引的字段,第一次可以添加成功, 会利用null自动填充

db.person.insert({name:'abs'}) 上面的创建成功 db.person.insert({name:'abc'})

创建失败,已经有一条age为null值的数据,因为创建的的是唯一索引,所以不能在创建一个age为null的数据


如果是给复合索引添加了唯一性,那么复合索引的组合不能重复,否则就不能添加

db.person.createIndex({name:1,age:-1},{unique:true})

db.person.insert({name:'as',age:18})

如果已经存在在库内,直接报错添加失败

索引的稀疏性

默认情况下MongoDB会给每一个文档都创建索引, 哪怕这个文档中没有指定索引的字段或者字段的取值是Null 但是这样大大增加了索引的体积, 所以为了进一步优化索引占用的存储空间, 我们可以创建稀疏索引 也就是只会为存在索引字段,并且索引字段取值不是null的文档创建索引

格式: db.<collection>.createIndex({<field>:<1 or -1>, ...}, {sparse:true}})

db.person.insert([
{name:'cs', age:19},
{name:'as', age:18},
{name:'bs', age:17}
])

db.person.createIndex({age:1},{unique:true})

db.person.insert({name:'test'}) // 添加成功

db.person.insert({name;'test'}) // 添加失败,因为age唯一性

  • 若索引具备了唯一性和稀疏性,那么就可以多次添加缺失了索引字段的文档了
  • 如果索引具备了稀疏性,那么就不会为缺失了索引字段或者索引字段取值是NULL的文档创建索引了,所以就不会冲突了

db.person.createIndex({age:1},{unique:true,sparse:true})

db.person.insert({name:'test'}) // 添加成功

db.person.insert({name:'test'}) // 添加成功

索引生存时间

针对日期字段或者包含日期的数组字段, 我们可以在创建索引的时候, 指定索引的生存时间, 一旦索引超过了指定的生存时间, 那么MongoDB会自动删除超过生存时间的文档

格式: db.<collection>.createIndex({<field>:<1 or -1>, ...}, {expireAfterSeconds:second}})


db.person.createIndex({addTime:1},{expireAfterSeconds:5}) // 当前索引生存5s

db.person.insert({name:'zs',addTime:new Date()}) // 超过5s自动删除当前的文档

注意点:

  1. MongoDB会定期清理超过时间的文档, 但是无法保证即时性(也就是设置的过期时间是1秒, 但是可能3秒后才会清除)
  2. 复合索引字段是不具备生存时间特性的, 也就是不能在复合索引中指定生存时间
  3. 当数组字段中包含多个日期, 我们给数组字段设置生存时间时, 系统会按照数组中最小的时间来计算生存时间

例如: {name:'it666', times:['2022-04-16 09:13:33','2022-04-16 07:13:33','2022-04-16 08:13:33']} 会按照'2022-04-16 07:13:33'来计算生存时间

删除索引

db.<collection>.dropIndex(<IndexName | IndexDefine>)

在mongoDB中没有删除索引的,如果需要修改索引,需要手动删除索引再添加新的索引

db.person.createIndex({name:1})

删除的第一种方式

使用db.person.getIndexes()拿到索引的name来删除

db.person.dropIndex('name_1')

第二种方式

使用添加索引的方式删除索引

db.person.dropIndex({name:1})

  • 如果是复合索引,必须和创建的时候一模一样才能删除成功索引
db.person.createIndex({name:1,age:-1})

db.person.dropIndex({name:1}) // 报错
db.person.dropIndex({age:-1}) // 报错
db.person.dropIndex({age:-1, name:1}) // 报错
db.person.dropIndex({name:1, age:-1}) // 不会报错

Released under the MIT License.