我有一个名为type_1_reimbursement.reimbursement_vendor_1_reimbursement.status_1_reimbursement.employee_note_1_company_id_1_created_1的索引,它是用以下命令创建的:

db.transaction.createIndex({ type: 1, 'reimbursement.reimbursement_vendor': 1, 'reimbursement.status': 1, 'reimbursement.employee_note': 1, company_id: 1,  created: 1 })

我的Collection 有大约600万张唱片.

我试着提出这个问题:

db.transaction.aggregate([
  {
    $match: {
      type: 'card',
      company_id: 'google',
      'reimbursement.reimbursement_vendor': { $ne: null },
      'reimbursement.status': { $in: ['approved', 'completed'] },
      'reimbursement.employee_note': {
        $nin: [null, ''],
      },
      created: { $gte: new ISODate('2022-08-12') },
    },
  },
  {
    $sort: { created: 1 },
  },
  {
    $limit: 1,
  },
]);

但是,它真的很慢.需要30-60秒才能跑完.这是解释结果

rs [direct: primary] application> db.transaction.aggregate([ { $match: { type: 'card', company_id: 'google', 'reimbursement.reimbursement_vendor': { $ne: null }, 'reimbursement.status': { $in: ['approved', 'completed'] }, 'reimbursement.employee_note': { $nin: [null, ''] }, created: { $gte: new ISODate('2022-08-12') } } }, { $sort: { created: 1 } }, { $limit: 1 }] ).explain();
{
  explainVersion: '1',
  queryPlanner: {
    namespace: 'application.transaction',
    indexFilterSet: false,
    parsedQuery: {
      '$and': [
        { company_id: { '$eq': 'google' } },
        { type: { '$eq': 'card' } },
        { created: { '$gte': ISODate("2022-08-12T00:00:00.000Z") } },
        {
          'reimbursement.status': { '$in': [ 'approved', 'completed' ] }
        },
        {
          'reimbursement.reimbursement_vendor': { '$not': { '$eq': null } }
        },
        {
          'reimbursement.employee_note': { '$not': { '$in': [ null, '' ] } }
        }
      ]
    },
    queryHash: '812E0D0B',
    planCacheKey: 'D506B9D0',
    optimizedPipeline: true,
    maxIndexedOrSolutionsReached: false,
    maxIndexedAndSolutionsReached: false,
    maxScansToExplodeReached: false,
    winningPlan: {
      stage: 'FETCH',
      inputStage: {
        stage: 'SORT',
        sortPattern: { created: 1 },
        memLimit: 104857600,
        limitAmount: 1,
        type: 'default',
        inputStage: {
          stage: 'IXSCAN',
          keyPattern: {
            type: 1,
            'reimbursement.reimbursement_vendor': 1,
            'reimbursement.status': 1,
            'reimbursement.employee_note': 1,
            company_id: 1,
            created: 1
          },
          indexName: 'type_1_reimbursement.reimbursement_vendor_1_reimbursement.status_1_reimbursement.employee_note_1_company_id_1_created_1',
          isMultiKey: false,
          multiKeyPaths: {
            type: [],
            'reimbursement.reimbursement_vendor': [],
            'reimbursement.status': [],
            'reimbursement.employee_note': [],
            company_id: [],
            created: []
          },
          isUnique: false,
          isSparse: false,
          isPartial: false,
          indexVersion: 2,
          direction: 'forward',
          indexBounds: {
            type: [ '["card", "card"]' ],
            'reimbursement.reimbursement_vendor': [ '[MinKey, undefined)', '(null, MaxKey]' ],
            'reimbursement.status': [
              '["approved", "approved"]',
              '["completed", "completed"]'
            ],
            'reimbursement.employee_note': [ '[MinKey, undefined)', '(null, "")', '("", MaxKey]' ],
            company_id: [ '["google", "google"]' ],
            created: [
              '[new Date(1660262400000), new Date(9223372036854775807)]'
            ]
          }
        }
      }
    },
    rejectedPlans: [
      {
        stage: 'SORT',
        sortPattern: { created: 1 },
        memLimit: 104857600,
        limitAmount: 1,
        type: 'simple',
        inputStage: {
          stage: 'FETCH',
          filter: { created: { '$gte': ISODate("2022-08-12T00:00:00.000Z") } },
          inputStage: {
            stage: 'IXSCAN',
            keyPattern: {
              type: 1,
              'reimbursement.reimbursement_vendor': 1,
              'reimbursement.status': 1,
              'reimbursement.employee_note': 1,
              company_id: 1
            },
            indexName: 'type_1_reimbursement.reimbursement_vendor_1_reimbursement.status_1_reimbursement.employee_note_1_company_id_1',
            isMultiKey: false,
            multiKeyPaths: {
              type: [],
              'reimbursement.reimbursement_vendor': [],
              'reimbursement.status': [],
              'reimbursement.employee_note': [],
              company_id: []
            },
            isUnique: false,
            isSparse: false,
            isPartial: false,
            indexVersion: 2,
            direction: 'forward',
            indexBounds: {
              type: [ '["card", "card"]' ],
              'reimbursement.reimbursement_vendor': [ '[MinKey, undefined)', '(null, MaxKey]' ],
              'reimbursement.status': [
                '["approved", "approved"]',
                '["completed", "completed"]'
              ],
              'reimbursement.employee_note': [ '[MinKey, undefined)', '(null, "")', '("", MaxKey]' ],
              company_id: [ '["google", "google"]' ]
            }
          }
        }
      }
    ]
  },
  command: {
    aggregate: 'transaction',
    pipeline: [
      {
        '$match': {
          type: 'card',
          company_id: 'google',
          'reimbursement.reimbursement_vendor': { '$ne': null },
          'reimbursement.status': { '$in': [ 'approved', 'completed' ] },
          'reimbursement.employee_note': { '$nin': [ null, '' ] },
          created: { '$gte': ISODate("2022-08-12T00:00:00.000Z") }
        }
      },
      { '$sort': { created: 1 } },
      { '$limit': 1 }
    ],
    cursor: {},
    '$db': 'application'
  },
  serverInfo: {
    host: '0ebbfaaef1f0',
    port: 27027,
    version: '6.0.4',
    gitVersion: '44ff59461c1353638a71e710f385a566bcd2f547'
  },
  serverParameters: {
    internalQueryFacetBufferSizeBytes: 104857600,
    internalQueryFacetMaxOutputDocSizeBytes: 104857600,
    internalLookupStageIntermediateDocumentMaxSizeBytes: 104857600,
    internalDocumentSourceGroupMaxMemoryBytes: 104857600,
    internalQueryMaxBlockingSortMemoryUsageBytes: 104857600,
    internalQueryProhibitBlockingMergeOnMongoS: 0,
    internalQueryMaxAddToSetBytes: 104857600,
    internalDocumentSourceSetWindowFieldsMaxMemoryBytes: 104857600
  },
  ok: 1,
  '$clusterTime': {
    clusterTime: Timestamp({ t: 1699662284, i: 1 }),
    signature: {
      hash: Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
      keyId: Long("0")
    }
  },
  operationTime: Timestamp({ t: 1699662284, i: 1 })
}


低于winningPlans,它看起来确实像是在使用指数,因为我看到它低于IXSCAN.然而,考虑到它的速度如此之慢,它似乎并不起作用.

我是否创建了错误的索引?

推荐答案

感谢您在提问中直接收集和分享您的解释方案.

winningPlans以下,它看起来像是在使用索引,因为我看到它在IXSCAN以下.然而,它似乎没有工作,因为它是多么缓慢.

数据库能够使用索引的事实并不自动意味着它能够使用索引efficiently.如果给定EXPLAIN计划的"executionStats"个详细程度,我们将能够更多地说明数据库正在做的具体工作量,但即使没有这一点,我们仍然可以看到当前的索引确实迫使它做一些额外的工作来满足请求.

try 通过更改键的顺序来重新创建索引,如下所示:

{ type: 1, company_id: 1, 'reimbursement.status': 1, created: 1, 'reimbursement.reimbursement_vendor': 1, 'reimbursement.employee_note': 1 }

这应该允许数据库消除阻塞SORT阶段(代之以流SORT_MERGE),同时还缩小了需要扫描的索引量.

有关该主题的更多一般指导,请参阅文档中的The ESR (Equality, Sort, Range) Rule page.

Mongodb相关问答推荐

获取响应周期中的特定键和值

如何在 MongoDB 中的集合下查找同一文档

Mongo $sort然后$group,顺序能保证吗?

多键索引,性能问题

如何匹配 MongoDB 中同一文档中两个字段的比较?

MongoDB - 来自先前匹配文档的聚合匹配字段

如何向所有文档添加一个字段,其中前 100 个文档的值为 1,接下来的 100 个文档的值为 2,依此类推?

使用 mongo-driver/mongo 使用键/值对中的值表达式查找文档

将 MongoDB 转移到另一台服务器?

MongoDb c# driver LINQ vs Native 查询

如何将查询结果(单个文档)存储到变量中?

Golang/mgo:如何让 MongoDB 在字段中使用当前时间?

在 Ubuntu 14.04 中安装 MongoDB 失败

在 GridFS、express、mongoDB、node.js 中存储来自 POST 请求的数据流

如何解决 ClassNotFoundException:com.mongodb.connection.BufferProvider?

如何根据其他字段添加条件模式?

如何使用node.js http服务器从mongodb返回大量行?

Ruby 按键值分组哈希

如何判断 MongoDB 中是否存在字段?

如何在 golang 和 mongodb 中通过 id 查找