MongoDB索引-初级篇

为什么要使用索引

下面以一个例子来说明: 假设我们现在有一个100w条的文档数据, 每个文档数据都包含一个username字段, 其值从user1user1000000, 假定我们希望通过username进行查询.

> db.user.find({username: "user101"})

我们可以在查询的同时开启操作的监控:

> db.user.find({username: "user101"}).explain()
{
  ...
  "nscanned": 1000000,
  "nscannedObjects": 1000000,
  "millis": 721,
  ...
}

我们可以看到集合中的所有的文档都会被扫描, 因为无法确定集合中的username字段是唯一的.

如果不使用索引的话, 我们可以通过限制查询结果数量来强制mongo找到符合条件的结果即返回.

> db.user.find({username: "user101"}).explain()
{
  ...
  "nscanned": 102,
  "nscannedObjects": 102,
  "millis": 2,
  ...
}

虽然上面的查询扫描数量减少了, 但是如果要查找的是user999999就还是需要整体扫描一遍.

创建索引

如下是按照username字段正序(指定为1)创建索引.

> db.user.ensureIndex({"username": 1})

由于创建索引比较费时, 可以通过在另一个shell中执行db.currentOp()或者是检查mongod的日志来 查看索引创建进度. 当索引创建完成后, 再次执行最初的查询.

> db.user.find({"username": "user101"}).explain()
{
  ...
  "nscanned": 1,
  "nscannedObjects": 1,
  "millis": 3,
  ...
}

使用了索引之后查询几乎可以瞬间完成, 但是每次写操作(包括插入, 更新和删除)将会耗费更多时间. 因为要同时更新文档和索引. 所以实际使用中, 不应该拥有两个以上的索引.

复合索引

就是在多个字段上创建一个索引, 注意索引间是有顺序的, 例如:

> db.user.ensureIndex({"age": 1, "username": 1})

实际创建的索引会先按照age字段排序, 然后按照username字段排序. 这个索引主要有以下三种使用方法:

点查询

> db.user.find({"age": 21}).sort({"username": -1})

由于之前我们创建了符合索引, 所以首先可以直接定位到age为21的记录, 然后由于这些记录是按照username正序排列好的, 所以可以直接用sort()方法指定逆序输出, 不需要额外的时间开销.

多值查询

> db.user.find({"age": {"$gte": 21, $"lte": 30}}).sort({"username": 1})

由于这个查询跨了多个age值, 虽然在每个age值内username是有序的, 但是整体来看是无序的, 所以需要在内存中先对结果进行排序, 然后才能返回. 所以说这个查询通常会较为低效.

隐式索引

我们现在创建了一个{"age": 1, "username": 1}的索引, 实际上我们同时也获得了一个{"age": 1}索引, 也就是说:

如果我们有一个N个键的索引, 那么想到哪关于我们同时获得了这N个键的前缀组成的索引.

复合索引创建的准则

用于精确匹配的字段, 放在索引的前面;用于范围匹配的字段放在最后.

假设现在要使用{"age": 1, "username": 1}的索引进行查询, 构造以下查询:

> db.user.find({"age": 47, "username": {"$gt": "user5", "$lt": "user8"}}).explain()

在基数比较高的键上建立索引, 至少要把基数高的键放在复合索引的前面

因为一个字段的基数(也就是取值的可能值)越高, 这个键上的索引就越有用. 举个例子, 比如我们在gender字段上创建索引, 那么只能将搜索空间缩小到50%左右; 而如果我们在name字段上创建索引, 那么结果集就会非常小, 查询时间大大缩短.

何时不应该使用索引

通常来说, 当查询返回较小的结果集时, 索引会非常高效. 结果集在原集合中所占比例越大, 索引的速度就越慢. 因为索引需要进行两次查找, 一次是查找索引条目, 一次是根据索引去查找文档. 一般来说, 当结果集占原集合30%左右时, 就需要考虑是否直接进行全表扫描来代替索引.

索引类型

唯一索引

可以在单个字段上或者符合字段上创建唯一索引, 被唯一索引标识的字段在集合中不能重复.

> db.user.ensureIndex({"username": 1}, {"unique": true})

稀疏索引

唯一索引会对该字段上所有的取值进行索引, 也就是说null也会被视为有值, 所以并不适合于在有缺失值的字段上使用, 而稀疏索引会忽略掉缺失值, 只对该字段存在值的部分文档进行索引.

> db.ensureIndex({"email": 1}, {"sparse": true})

当然, 可以与唯一索引结合使用

> db.ensureIndex({"email": 1}, {"unique": true, "sparse": true})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值