Skip to content

图书馆的“索引目录”:MongoDB查询为什么这么快?

1. 什么是索引

假设图书馆里有 100万本书,现在让管理员帮你找一本《三体》。

如果图书馆没有任何分类和目录,管理员只能:

第1本、第2本、第3本……

一本一本找。

100万本书,要找到什么时候?

但现实中的图书馆不会这么干。通常会按照书名、作者、分类等建立目录。

管理员只需要:

《三体》 → 科幻类 → A区 → 12号书架

很快就能找到。

数据库中的索引,就是类似图书馆目录的东西。

索引可以帮助 MongoDB 更快地定位需要查询的数据,而不需要每次都把所有数据找一遍。

2. 为什么需要索引

假设有一个 books 集合:

js
{
    name: "三体",
    author: "刘慈欣",
    category: "科幻"
}

现在我们经常需要根据书名查询:

js
db.books.find({ name: "三体" })

如果 name 没有建立索引,MongoDB 可能需要检查大量文档,判断它们的 name 是否是 "三体"

这种方式就像:

管理员拿着书架上的书,一本一本检查。

数据量越大,需要检查的数据越多,查询可能就越慢。

如果我们给 name 建立索引:

js
db.books.createIndex({ name: 1 })

就相当于:

给图书馆建立了一本“按照书名查找”的目录。

再次查询:

js
db.books.find({ name: "三体" })

MongoDB 就可以利用这个索引快速定位相关数据。

3. MongoDB中如何创建索引

MongoDB创建索引非常简单:

js
db.books.createIndex({ name: 1 })

这里的 1 表示升序索引

也可以创建降序索引:

js
db.books.createIndex({ name: -1 })

对于简单的等值查询来说,初学阶段可以先记住:

1 表示升序,-1 表示降序。

例如,我们经常根据作者查询:

js
db.books.find({ author: "刘慈欣" })

那么可以建立:

js
db.books.createIndex({ author: 1 })

这就相当于图书馆又增加了一本:

“按照作者查找”的目录。

4. 一个集合可以有多个索引吗?

当然可以。

例如:

js
db.books.createIndex({ name: 1 })

db.books.createIndex({ author: 1 })

db.books.createIndex({ category: 1 })

现在图书馆就有了三本目录:

text
书名目录
作者目录
分类目录

用户可以根据不同条件快速查找。

例如:

js
// 根据书名查
db.books.find({ name: "三体" })

// 根据作者查
db.books.find({ author: "刘慈欣" })

// 根据分类查
db.books.find({ category: "科幻" })

不过,索引并不是越多越好。

5. 索引的代价

索引虽然可以提高查询速度,但并不是没有代价。

① 占用额外空间

回到图书馆的例子。

为了方便找书,我们建立了很多目录:

书名目录、作者目录、分类目录……

这些目录本身也需要占用空间。

MongoDB的索引同样需要占用额外的存储空间。

② 增加写操作的成本

假设图书馆新进了一本《三体》。

管理员不仅需要:

把书放到书架上

还需要:

更新书名目录、作者目录、分类目录……

MongoDB也是一样。

当我们新增、修改或者删除数据时,如果相关字段建立了索引,MongoDB还需要维护对应的索引

因此:

索引可以提高查询性能,但也会增加写操作的维护成本。

③ 索引不是越多越好

如果图书馆为了方便查找,给每一个属性都建立目录:

text
书名目录
作者目录
出版社目录
价格目录
出版时间目录
页数目录
……

目录越来越多,虽然查找方式越来越丰富,但管理员维护这些目录也会越来越麻烦。

MongoDB也是一样。

索引过多可能导致:

  • 占用更多存储空间
  • 增加数据写入成本
  • 增加索引维护开销
  • 一些索引可能长期都用不到

所以,建立索引应该根据实际的查询场景来决定。

6. 如何查看索引

我们可以使用:

js
db.books.getIndexes()

查看当前集合的索引。

例如:

js
db.books.getIndexes()

可能得到:

js
[
    {
        name: "_id_",
        key: {
            _id: 1
        }
    },
    {
        name: "name_1",
        key: {
            name: 1
        }
    }
]

可以看到,MongoDB默认会为 _id 创建索引。

我们自己创建的 name 索引也出现在其中。

7. 怎么知道查询有没有使用索引?

MongoDB提供了一个非常有用的方法:

js
explain()

例如:

js
db.books
    .find({ name: "三体" })
    .explain("executionStats")

它可以帮助我们了解 MongoDB 是如何执行这次查询的。

初学阶段只需要先记住两个概念:

text
COLLSCAN

表示扫描集合中的大量文档。

可以简单理解为:

管理员一本一本找书。

而:

text
IXSCAN

表示使用了索引扫描。

可以理解为:

管理员先查目录,再根据目录找到书。

因此,当我们发现一个查询很慢时,可以使用 explain() 帮助分析:

MongoDB到底有没有使用我们创建的索引?

8. 总结

索引就像图书馆里的查询目录

没有索引:

管理员只能一本一本找。

有了索引:

管理员可以先查目录,再快速定位目标。

MongoDB中的索引可以明显提高很多查询场景的效率,但索引也不是免费的:

它需要占用空间,也会增加数据写入时的维护成本。

所以,索引并不是越多越好,而应该根据实际业务中的查询条件和访问频率合理创建。

简单来说:

查询是“找书”,索引是“目录”,而 explain() 就像是查看管理员到底有没有查目录。

这就是 MongoDB 索引最基本的概念。

上次更新于: