MongoDB 入门指南二:索引 —— 让查询速度飞起来

发布于:2025-08-17 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、什么是索引?为什么需要它?

想象一下,你有一本厚厚的字典(相当于 MongoDB 的集合),如果想查 “MongoDB” 这个词,没有目录(索引)的话,你得从第一页翻到最后一页,多费劲!

索引就是数据库的 “目录”,它能让 MongoDB 在查询数据时,不用扫描整个集合,直接定位到目标数据,大大提高查询速度。
没有索引的问题:
当集合数据量很大(比如 100 万条评论),查询时会扫描所有文档(全集合扫描),速度慢得让人崩溃。
有了索引,哪怕数据再多,也能像查字典一样快速找到目标。
在这里插入图片描述

  • 无索引查询:扫描集合中每个文档(全集合扫描),效率极低
  • 有索引查询:直接定位目标数据,查询速度提升10-100倍
  • 底层原理:MongoDB使用B-Tree数据结构存储索引(类似MySQL的B+Tree)

当数据量超过10万条时,索引对查询速度的影响会变得非常明显

二、常见的索引类型(小白必学 3 种)

MongoDB 支持多种索引,最常用的是前两种:

1. 单字段索引(最常用)

在单个字段上创建的索引,比如给userid或likenum字段建索引。

# 语法:db.集合名.createIndex({ 字段名: 排序方式 })
# 1表示升序,-1表示降序(单字段索引中排序方式影响不大)
db.comment.createIndex({ "userid": 1 })  # 给userid字段建升序索引

作用:加速对userid字段的查询,比如db.comment.find({ “userid”: “1003” })会快很多。

2. 复合索引(多字段组合)

当查询条件涉及多个字段时,用复合索引。

# 语法:db.集合名.createIndex({ 字段1: 排序方式, 字段2: 排序方式 })
db.comment.createIndex({ "userid": 1, "likenum": -1 })

关键点:字段顺序很重要!
上面的索引会先按userid升序排序,在相同 userid 内,再按likenum降序排序。
适合查询:db.comment.find({ “userid”: “1003”, “likenum”: { $gt: 10 } })

3. 其他特殊索引(了解即可)

  • 文本索引:专门用于全文搜索(比如查评论内容包含 “好用” 的文档),如db.articles.createIndex({content: “text”})
  • 地理空间索引:用于定位地理位置(比如 “附近的人” 功能),db.places.createIndex({loc: “2dsphere”})。
  • 哈希索引:对字段值哈希后建索引,适合分片集群,不支持范围查询,如db.users.createIndex({username: “hashed”})。

三、索引的管理:查看、创建、删除

1. 查看集合中的索引

# 查看comment集合的所有索引
db.comment.getIndexes()

// 结果示例
[
  {
    "v": 2,                   // 索引版本
    "key": { "_id": 1 },       // 索引字段及排序
    "name": "_id_",            // 索引名称
    "ns": "articledb.comment"  // 所属集合
  }
]

刚创建的集合会默认有一个_id索引(主键索引),确保_id不重复,不用手动创建。

2. 创建索引(重点)

# 1. 单字段索引:给articleid字段建索引
db.comment.createIndex({ "articleid": 1 })

# 2. 复合索引:给userid和createdatetime建索引
db.comment.createIndex({ "userid": 1, "createdatetime": -1 })

# 3. 唯一索引:确保字段值不重复(比如不允许相同的userid+articleid重复评论)
db.comment.createIndex({ "userid": 1, "articleid": 1 }, { unique: true })

常用参数(options):

  • unique: true:创建唯一索引,避免重复数据(如上面的例子)。
  • background: true:后台创建索引,不阻塞其他操作(数据量大时推荐)。

3. 删除索引

# 方法1:通过索引名称删除(索引名称可通过getIndexes()查看)
db.comment.dropIndex("userid_1")  # 删除userid的单字段索引

# 方法2:通过索引规则删除
db.comment.dropIndex({ "userid": 1, "likenum": -1 })  # 删除复合索引

四、怎么知道索引生效了?用执行计划!

创建索引后,怎么确认它真的被用到了?用explain()查看执行计划:

# 查看查询是否使用了索引
db.comment.find({ "userid": "1003" }).explain("executionStats")

关注关键输出:

"winningPlan": {
  "stage": "FETCH",          // 数据获取阶段
  "inputStage": {
    "stage": "IXSCAN",       // 索引扫描
    "indexName": "userid_1", // 使用的索引
    "direction": "forward"   // 扫描方向
  }
},
"executionStats": {
  "executionTimeMillis": 0,  // 执行时间(毫秒)
  "totalKeysExamined": 1,    // 扫描索引键数
  "totalDocsExamined": 1     // 扫描文档数
}

看这两个关键指标:
executionStats.executedScannedDocuments:扫描的文档数。
有索引:这个数字很小(接近查询结果数)。
没索引:这个数字等于集合总文档数(全表扫描)。

五、小技巧:“涵盖的查询” 更高效

如果查询的字段全都是索引字段,MongoDB 会直接从索引中取数据,不用访问原始文档,速度超快!

# 假设已有索引:{ "userid": 1, "nickname": 1 }
# 查询和投影都只用到索引字段
db.comment.find(
  { "userid": "1003" },  # 查询条件
  { "userid": 1, "nickname": 1, "_id": 0 }  # 只返回这两个字段
)

总结:索引的 “利与弊”

  • :大幅提高查询速度,尤其是数据量大的时候。
  • :占用额外存储空间,且会降低插入 / 更新 / 删除的速度(因为要维护索引)。

最佳实践:

  • 只为常用查询字段建索引。
  • 复合索引的字段顺序要符合查询习惯(频繁过滤的字段放前面)。
  • 定期用explain()检查索引是否被有效使用。

网站公告

今日签到

点亮在社区的每一天
去签到