前言

作为资料库管理的新手小白,在 MongoDB 应用方面实在没什么经验,採用 MongoDB 也只是为了方便储存不对称数据的优势,然而使用到现几个月了逐渐发现效能堪忧,于是开始了我的性能调校之路!

情境描述

在处理 AWS 帐单数据的 MongoDB 系统中,我们遇到了查询效能缓慢的问题,系统需要频繁地根据帐单期间(BillingPeriodStartDate)和帐户 ID(UsageAccountId)来查询数据,单一 collection 数据量已达到约 300 万笔记录。

问题诊断

1. 初始查询分析

首先,我的需求是使用以下查询来检索特定帐期和帐户的数据:

db.rawdata.find({
"lineItem/UsageAccountId": {
$in: [
"123456789001", "123456789002", "123456789003",
// ... 更多帐户 ID
]
},
"bill/BillingPeriodStartDate": "2024-11-01T00:00:00Z"
})

2. 使用 explain() 诊断查询效能

通过添加 explain("executionStats") 来分析查询执行情况:

db.rawdata.find({...}).explain("executionStats")

优化前的执行统计:

{
"executionStats": {
"executionSuccess": true,
"nReturned": 133696,
"executionTimeMillis": 8547,
"totalKeysExamined": 0,
"totalDocsExamined": 3109528,
"executionStages": {
"stage": "COLLSCAN",
// ... 省略其他细节
}
}
}

关键问题指标:

  • executionTimeMillis: 8547 (查询耗时近 8.5 秒)
  • stage: "COLLSCAN" (进行了全集合扫描)
  • totalDocsExamined: 3109528 (扫描了大量文档)
  • totalKeysExamined: 0 (没有使用索引)
  • 解决方案

    1. 创建复合索引

    根据查询需求,我创建了一个复合索引:

    db.rawdata.createIndex({
    "bill/BillingPeriodStartDate": 1,
    "lineItem/UsageAccountId": 1
    })

    2. 验证索引创建

    确认索引是否成功创建:

    db.rawdata.getIndexes()

    3. 重新执行查询分析

    使用相同的 explain 命令检查优化效果:

    db.rawdata.find({...}).explain("executionStats")

    优化后的执行统计:

    {
    "executionStats": {
    "executionSuccess": true,
    "nReturned": 133696,
    "executionTimeMillis": 1247,
    "totalKeysExamined": 133730,
    "totalDocsExamined": 133696,
    "executionStages": {
    "stage": "FETCH",
    "inputStage": {
    "stage": "IXSCAN",
    // ... 省略其他细节
    }
    }
    }
    }

    效能改善分析

    通过对比优化前后的关键指标:

  • 查询耗时:

    • 优化前:8547 毫秒
    • 优化后:1247 毫秒
    • 改善幅度:约 85%
  • 扫描文档数:

    • 优化前:3,109,528 文档
    • 优化后:133,730 文档
    • 减少幅度:约 95%
  • 查询方式:

    • 优化前:COLLSCAN(全集合扫描)
    • 优化后:IXSCAN(索引扫描)
  • 小结

    本文透过 explain() 分析查询效能低落的癥结点,并建立有效索引,达到巨幅提升查询的效能,但这仅仅只是调优的一小部分,我目前还遇到其他问题尚未解决,在问题解决以后,会再跟大家分享,请各位敬请期待!