2016-11-30 45 views
3

我与具有以下文件设计数据库工作:正确索引数组字段?

{ 
    'email': '[email protected]', 
    'credentials': [{ 
     'type':  'password', 
     'content': 'hashedpassword' 
    }, { 
     'type':  'oauth2', 
     'content': 'token' 
    }] 
} 

我已经索引{credentials.type: 1, credentials.content: 1}。它被正确地拾取,但是在50k文档集合上表现糟糕。

这里是指示查询计划日志:

[conn73] command database.users command: find { 
    find: "users", 
    filter: { 
     credentials.type: "type", 
     credentials.content: "content" 
    }, 
    limit: 1, 
    batchSize: 1, 
    singleBatch: true 
} 
planSummary: IXSCAN { 
    credentials.type: 1, 
    credentials.content: 1 
} 
keysExamined:20860 
docsExamined:18109 
cursorExhausted:1 
keyUpdates:0 
writeConflicts:0 
numYields:163 
nreturned:1 
reslen:455 
locks:{ 
    Global: { 
     acquireCount: { 
      r: 328 
     } 
    }, 
    Database: { 
     acquireCount: { 
      r: 164 
     } 
    }, 
    Collection: { 
     acquireCount: { 
      r: 164 
     } 
    } 
} 
protocol:op_query 
331ms 

我注意到有大量keysExamined和docsExamined的。我知道mongodb能够将所有的值放入数组中来构建索引。为什么它必须扫描如此多的密钥?

我确实有很高的并发访问,但只读。

下面是解释查询的()结果:

> db.users.find({'credentials.type': 'abc', 'credentials.content': 'def'}).explain() 
{ 
    "queryPlanner" : { 
     "plannerVersion" : 1, 
     "namespace" : "net.users", 
     "indexFilterSet" : false, 
     "parsedQuery" : { 
      "$and" : [ 
       { 
        "credentials.type" : { 
         "$eq" : "abc" 
        } 
       }, 
       { 
        "credentials.content" : { 
         "$eq" : "def" 
        } 
       } 
      ] 
     }, 
     "winningPlan" : { 
      "stage" : "FETCH", 
      "filter" : { 
       "credentials.content" : { 
        "$eq" : "def" 
       } 
      }, 
      "inputStage" : { 
       "stage" : "IXSCAN", 
       "keyPattern" : { 
        "credentials.type" : 1, 
        "credentials.content" : 1 
       }, 
       "indexName" : "credentials.type_1_credentials.content_1", 
       "isMultiKey" : true, 
       "isUnique" : false, 
       "isSparse" : false, 
       "isPartial" : false, 
       "indexVersion" : 1, 
       "direction" : "forward", 
       "indexBounds" : { 
        "credentials.type" : [ 
         "[\"abc\", \"abc\"]" 
        ], 
        "credentials.content" : [ 
         "[MinKey, MaxKey]" 
        ] 
       } 
      } 
     }, 
     "rejectedPlans" : [ ] 
    }, 
    "serverInfo" : { 
     "host" : "localhost", 
     "port" : 27017, 
     "version" : "3.2.11", 
     "gitVersion" : "009580ad490190ba33d1c6253ebd8d91808923e4" 
    }, 
    "ok" : 1 
} 

我跑的MongoDB v3.2.11。我如何正确优化此查询?我应该改变文件设计吗?

+0

以及现有索引{credentials.type:1,credentials.content:1}您可以在{credentials:1}上拥有索引 –

+0

您可以详细说明您的查询和所需的结果文档吗?也请发帖query.explain() – sergiuz

+0

@SergiuZaharie我已经更新,包括查询解释。 –

回答

0

您可以尝试将凭据分为不同的文档。

如:

{ 
    'email': '[email protected]', 
    'credentialType':  'password', 
    'credentialContent': 'hashedpassword' 
} 

{ 
    'email': '[email protected]', 
    'credentialType':  'oauth2', 
    'credentialContent': 'token' 
} 

和创建credentialType和credentialContent指标。

这样你会有更多的文档,但更清晰的索引。你的查询会运行得更快。因为它不需要处理obj数组。

+0

有没有关于数组对象索引应该如何慢的参考? –

0

感谢Sergiu Zaharie的提示,我能够重新审视索引问题。

事实证明,由于'credentials.type'都是相似的,'credentials.content'都是不同的,我应该首先将复合索引放在'credentials.content'上。

换句话说,{credentials.content: 1, credentials.type: 1}就是这里的答案。

+0

使用复合索引时,查询中字段的顺序无关紧要,它仅在排序上很重要。这里的例子https://docs.mongodb.com/v3.2/tutorial/sort-results-with-indexes/#sort-and-non-prefix-subset-of-an-index – sergiuz

+0

它看起来像一个独特的索引字段导致更好的性能比在一个有少量选择的字段上索引。我原本以为这个命令并不重要,我也没有在其他地方学过。我认为索引字段是连接的,但事实并非如此。看起来指数中的第一个领域非常重要,最好接近独特。 –