了解Mongoid查询计划

问题描述 投票:2回答:1

我对Mongoid和Rails的经验很少。我正在尝试使自己在Rails应用程序中编写的查询表现得尽可能出色。

该应用程序具有User模型和WithdrawalHold模型。一个用户可以有许多提款保留。

WithdrawalHold具有user_idhold_until字段。 WithdrawalHold具有以下索引:index({ user_id: 1, hold_until: 1, _id: -1 }, background: true)index({ hold_until: 1 }, background: true)

[当我在Rails控制台中运行以下命令时:WithdrawalHold.where(:user_id => 1, :hold_until.gte => Time.now).order_by(:hold_until => 'asc').explain

{"queryPlanner"=>
  {"plannerVersion"=>1,
   "namespace"=>"test.limits_withdrawal_holds",
   "indexFilterSet"=>false,
   "parsedQuery"=>{"$and"=>[{"user_id"=>{"$eq"=>1}}, {"hold_until"=>{"$gte"=>2018-02-25 21:53:24 UTC}}]},
   "winningPlan"=>
    {"stage"=>"FETCH",
     "inputStage"=>
      {"stage"=>"IXSCAN",
       "keyPattern"=>{"user_id"=>1, "hold_until"=>1, "_id"=>-1},
       "indexName"=>"user_id_1_hold_until_1__id_-1",
       "isMultiKey"=>false,
       "multiKeyPaths"=>{"user_id"=>[], "hold_until"=>[], "_id"=>[]},
       "isUnique"=>false,
       "isSparse"=>false,
       "isPartial"=>false,
       "indexVersion"=>2,
       "direction"=>"forward",
       "indexBounds"=>{"user_id"=>["[1, 1]"], "hold_until"=>["[new Date(1519595604368), new Date(9223372036854775807)]"], "_id"=>["[MaxKey, MinKey]"]}}},
   "rejectedPlans"=>
    [{"stage"=>"FETCH",
      "filter"=>{"user_id"=>{"$eq"=>1}},
      "inputStage"=>
       {"stage"=>"IXSCAN",
        "keyPattern"=>{"hold_until"=>1},
        "indexName"=>"hold_until_1",
        "isMultiKey"=>false,
        "multiKeyPaths"=>{"hold_until"=>[]},
        "isUnique"=>false,
        "isSparse"=>false,
        "isPartial"=>false,
        "indexVersion"=>2,
        "direction"=>"forward",
        "indexBounds"=>{"hold_until"=>["[new Date(1519595604368), new Date(9223372036854775807)]"]}}}]},
 "executionStats"=>
  {"executionSuccess"=>true,
   "nReturned"=>0,
   "executionTimeMillis"=>0,
   "totalKeysExamined"=>0,
   "totalDocsExamined"=>0,
   "executionStages"=>
    {"stage"=>"FETCH",
     "nReturned"=>0,
     "executionTimeMillisEstimate"=>0,
     "works"=>2,
     "advanced"=>0,
     "needTime"=>0,
     "needYield"=>0,
     "saveState"=>0,
     "restoreState"=>0,
     "isEOF"=>1,
     "invalidates"=>0,
     "docsExamined"=>0,
     "alreadyHasObj"=>0,
     "inputStage"=>
      {"stage"=>"IXSCAN",
       "nReturned"=>0,
       "executionTimeMillisEstimate"=>0,
       "works"=>1,
       "advanced"=>0,
       "needTime"=>0,
       "needYield"=>0,
       "saveState"=>0,
       "restoreState"=>0,
       "isEOF"=>1,
       "invalidates"=>0,
       "keyPattern"=>{"user_id"=>1, "hold_until"=>1, "_id"=>-1},
       "indexName"=>"user_id_1_hold_until_1__id_-1",
       "isMultiKey"=>false,
       "multiKeyPaths"=>{"user_id"=>[], "hold_until"=>[], "_id"=>[]},
       "isUnique"=>false,
       "isSparse"=>false,
       "isPartial"=>false,
       "indexVersion"=>2,
       "direction"=>"forward",
       "indexBounds"=>{"user_id"=>["[1, 1]"], "hold_until"=>["[new Date(1519595604368), new Date(9223372036854775807)]"], "_id"=>["[MaxKey, MinKey]"]},
       "keysExamined"=>0,
       "seeks"=>1,
       "dupsTested"=>0,
       "dupsDropped"=>0,
       "seenInvalidated"=>0}},
   "allPlansExecution"=>
    [{"nReturned"=>0,
      "executionTimeMillisEstimate"=>0,
      "totalKeysExamined"=>1,
      "totalDocsExamined"=>1,
      "executionStages"=>
       {"stage"=>"FETCH",
        "filter"=>{"user_id"=>{"$eq"=>1}},
        "nReturned"=>0,
        "executionTimeMillisEstimate"=>0,
        "works"=>1,
        "advanced"=>0,
        "needTime"=>1,
        "needYield"=>0,
        "saveState"=>0,
        "restoreState"=>0,
        "isEOF"=>0,
        "invalidates"=>0,
        "docsExamined"=>1,
        "alreadyHasObj"=>0,
        "inputStage"=>
         {"stage"=>"IXSCAN",
          "nReturned"=>1,
          "executionTimeMillisEstimate"=>0,
          "works"=>1,
          "advanced"=>1,
          "needTime"=>0,
          "needYield"=>0,
          "saveState"=>0,
          "restoreState"=>0,
          "isEOF"=>0,
          "invalidates"=>0,
          "keyPattern"=>{"hold_until"=>1},
          "indexName"=>"hold_until_1",
          "isMultiKey"=>false,
          "multiKeyPaths"=>{"hold_until"=>[]},
          "isUnique"=>false,
          "isSparse"=>false,
          "isPartial"=>false,
          "indexVersion"=>2,
          "direction"=>"forward",
          "indexBounds"=>{"hold_until"=>["[new Date(1519595604368), new Date(9223372036854775807)]"]},
          "keysExamined"=>1,
          "seeks"=>1,
          "dupsTested"=>0,
          "dupsDropped"=>0,
          "seenInvalidated"=>0}}},
     {"nReturned"=>0,
      "executionTimeMillisEstimate"=>0,
      "totalKeysExamined"=>0,
      "totalDocsExamined"=>0,
      "executionStages"=>
       {"stage"=>"FETCH",
        "nReturned"=>0,
        "executionTimeMillisEstimate"=>0,
        "works"=>1,
        "advanced"=>0,
        "needTime"=>0,
        "needYield"=>0,
        "saveState"=>0,
        "restoreState"=>0,
        "isEOF"=>1,
        "invalidates"=>0,
        "docsExamined"=>0,
        "alreadyHasObj"=>0,
        "inputStage"=>
         {"stage"=>"IXSCAN",
          "nReturned"=>0,
          "executionTimeMillisEstimate"=>0,
          "works"=>1,
          "advanced"=>0,
          "needTime"=>0,
          "needYield"=>0,
          "saveState"=>0,
          "restoreState"=>0,
          "isEOF"=>1,
          "invalidates"=>0,
          "keyPattern"=>{"user_id"=>1, "hold_until"=>1, "_id"=>-1},
          "indexName"=>"user_id_1_hold_until_1__id_-1",
          "isMultiKey"=>false,
          "multiKeyPaths"=>{"user_id"=>[], "hold_until"=>[], "_id"=>[]},
          "isUnique"=>false,
          "isSparse"=>false,
          "isPartial"=>false,
          "indexVersion"=>2,
          "direction"=>"forward",
          "indexBounds"=>{"user_id"=>["[1, 1]"], "hold_until"=>["[new Date(1519595604368), new Date(9223372036854775807)]"], "_id"=>["[MaxKey, MinKey]"]},
          "keysExamined"=>0,
          "seeks"=>1,
          "dupsTested"=>0,
          "dupsDropped"=>0,
          "seenInvalidated"=>0}}}]},
 "serverInfo"=>{"host"=>"1cf25211760e", "port"=>27017, "version"=>"3.4.9", "gitVersion"=>"876ebee8c7dd0e2d992f36a848ff4dc50ee6603e"},
 "ok"=>1.0}

结果表明使用了{ user_id: 1, hold_until: 1, _id: -1 }索引。并且检查的文档数为0,我相信这表明该查询已包含在索引(https://docs.mongodb.com/manual/reference/explain-results/)中。

注意,在上面的查询中,user_id为1,并且没有该ID的用户。

现在,如果我使用真实的user_id(其类型为BSON :: ObjectId):

WithdrawalHold.where(:user_id => user.id, :hold_until.gte => Time.now).order_by(:hold_until => 'asc').explain

{"queryPlanner"=>
   {"plannerVersion"=>1,
    "namespace"=>"test.limits_withdrawal_holds",
    "indexFilterSet"=>false,
    "parsedQuery"=>{"$and"=>[{"user_id"=>{"$eq"=>BSON::ObjectId('5a932ba8476e0a3ee86a3b5e')}}, {"hold_until"=>{"$gte"=>2018-02-25 21:55:05 UTC}}]},
    "winningPlan"=>
     {"stage"=>"FETCH",
      "filter"=>{"user_id"=>{"$eq"=>BSON::ObjectId('5a932ba8476e0a3ee86a3b5e')}},
      "inputStage"=>
       {"stage"=>"IXSCAN",
        "keyPattern"=>{"hold_until"=>1},
        "indexName"=>"hold_until_1",
        "isMultiKey"=>false,
        "multiKeyPaths"=>{"hold_until"=>[]},
        "isUnique"=>false,
        "isSparse"=>false,
        "isPartial"=>false,
        "indexVersion"=>2,
        "direction"=>"forward",
        "indexBounds"=>{"hold_until"=>["[new Date(1519595705482), new Date(9223372036854775807)]"]}}},
    "rejectedPlans"=>
     [{"stage"=>"FETCH",
       "inputStage"=>
        {"stage"=>"IXSCAN",
         "keyPattern"=>{"user_id"=>1, "hold_until"=>1, "_id"=>-1},
         "indexName"=>"user_id_1_hold_until_1__id_-1",
         "isMultiKey"=>false,
         "multiKeyPaths"=>{"user_id"=>[], "hold_until"=>[], "_id"=>[]},
         "isUnique"=>false,
         "isSparse"=>false,
         "isPartial"=>false,
         "indexVersion"=>2,
         "direction"=>"forward",
         "indexBounds"=>
          {"user_id"=>["[ObjectId('5a932ba8476e0a3ee86a3b5e'), ObjectId('5a932ba8476e0a3ee86a3b5e')]"],
           "hold_until"=>["[new Date(1519595705482), new Date(9223372036854775807)]"],
           "_id"=>["[MaxKey, MinKey]"]}}}]},
  "executionStats"=>
   {"executionSuccess"=>true,
    "nReturned"=>3,
    "executionTimeMillis"=>0,
    "totalKeysExamined"=>3,
    "totalDocsExamined"=>3,
    "executionStages"=>
     {"stage"=>"FETCH",
      "filter"=>{"user_id"=>{"$eq"=>BSON::ObjectId('5a932ba8476e0a3ee86a3b5e')}},
      "nReturned"=>3,
      "executionTimeMillisEstimate"=>0,
      "works"=>5,
      "advanced"=>3,
      "needTime"=>0,
      "needYield"=>0,
      "saveState"=>0,
      "restoreState"=>0,
      "isEOF"=>1,
      "invalidates"=>0,
      "docsExamined"=>3,
      "alreadyHasObj"=>0,
      "inputStage"=>
       {"stage"=>"IXSCAN",
        "nReturned"=>3,
        "executionTimeMillisEstimate"=>0,
        "works"=>4,
        "advanced"=>3,
        "needTime"=>0,
        "needYield"=>0,
        "saveState"=>0,
        "restoreState"=>0,
        "isEOF"=>1,
        "invalidates"=>0,
        "keyPattern"=>{"hold_until"=>1},
        "indexName"=>"hold_until_1",
        "isMultiKey"=>false,
        "multiKeyPaths"=>{"hold_until"=>[]},
        "isUnique"=>false,
        "isSparse"=>false,
        "isPartial"=>false,
        "indexVersion"=>2,
        "direction"=>"forward",
        "indexBounds"=>{"hold_until"=>["[new Date(1519595705482), new Date(9223372036854775807)]"]},
        "keysExamined"=>3,
        "seeks"=>1,
        "dupsTested"=>0,
        "dupsDropped"=>0,
        "seenInvalidated"=>0}},
    "allPlansExecution"=>
     [{"nReturned"=>3,
       "executionTimeMillisEstimate"=>0,
       "totalKeysExamined"=>3,
       "totalDocsExamined"=>3,
       "executionStages"=>
        {"stage"=>"FETCH",
         "nReturned"=>3,
         "executionTimeMillisEstimate"=>0,
         "works"=>4,
         "advanced"=>3,
         "needTime"=>0,
         "needYield"=>0,
         "saveState"=>0,
         "restoreState"=>0,
         "isEOF"=>1,
         "invalidates"=>0,
         "docsExamined"=>3,
         "alreadyHasObj"=>0,
         "inputStage"=>
          {"stage"=>"IXSCAN",
           "nReturned"=>3,
           "executionTimeMillisEstimate"=>0,
           "works"=>4,
           "advanced"=>3,
           "needTime"=>0,
           "needYield"=>0,
           "saveState"=>0,
           "restoreState"=>0,
           "isEOF"=>1,
           "invalidates"=>0,
           "keyPattern"=>{"user_id"=>1, "hold_until"=>1, "_id"=>-1},
           "indexName"=>"user_id_1_hold_until_1__id_-1",
           "isMultiKey"=>false,
           "multiKeyPaths"=>{"user_id"=>[], "hold_until"=>[], "_id"=>[]},
           "isUnique"=>false,
           "isSparse"=>false,
           "isPartial"=>false,
           "indexVersion"=>2,
           "direction"=>"forward",
           "indexBounds"=>
            {"user_id"=>["[ObjectId('5a932ba8476e0a3ee86a3b5e'), ObjectId('5a932ba8476e0a3ee86a3b5e')]"],
             "hold_until"=>["[new Date(1519595705482), new Date(9223372036854775807)]"],
             "_id"=>["[MaxKey, MinKey]"]},
           "keysExamined"=>3,
           "seeks"=>1,
           "dupsTested"=>0,
           "dupsDropped"=>0,
           "seenInvalidated"=>0}}},
      {"nReturned"=>3,
       "executionTimeMillisEstimate"=>0,
       "totalKeysExamined"=>3,
       "totalDocsExamined"=>3,
       "executionStages"=>
        {"stage"=>"FETCH",
         "filter"=>{"user_id"=>{"$eq"=>BSON::ObjectId('5a932ba8476e0a3ee86a3b5e')}},
         "nReturned"=>3,
         "executionTimeMillisEstimate"=>0,
         "works"=>4,
         "advanced"=>3,
         "needTime"=>0,
         "needYield"=>0,
         "saveState"=>0,
         "restoreState"=>0,
         "isEOF"=>1,
         "invalidates"=>0,
         "docsExamined"=>3,
         "alreadyHasObj"=>0,
         "inputStage"=>
          {"stage"=>"IXSCAN",
           "nReturned"=>3,
           "executionTimeMillisEstimate"=>0,
           "works"=>4,
           "advanced"=>3,
           "needTime"=>0,
           "needYield"=>0,
           "saveState"=>0,
           "restoreState"=>0,
           "isEOF"=>1,
           "invalidates"=>0,
           "keyPattern"=>{"hold_until"=>1},
           "indexName"=>"hold_until_1",
           "isMultiKey"=>false,
           "multiKeyPaths"=>{"hold_until"=>[]},
           "isUnique"=>false,
           "isSparse"=>false,
           "isPartial"=>false,
           "indexVersion"=>2,
           "direction"=>"forward",
           "indexBounds"=>{"hold_until"=>["[new Date(1519595705482), new Date(9223372036854775807)]"]},
           "keysExamined"=>3,
           "seeks"=>1,
           "dupsTested"=>0,
           "dupsDropped"=>0,
           "seenInvalidated"=>0}}}]},
  "serverInfo"=>{"host"=>"1cf25211760e", "port"=>27017, "version"=>"3.4.9", "gitVersion"=>"876ebee8c7dd0e2d992f36a848ff4dc50ee6603e"},
  "ok"=>1.0}

结果表明仅使用{ hold_until: 1 }索引,并且检查了许多文档。

我想了解为什么两个查询的计划有所不同,以及是否有可能编写此查询以使其完全被索引覆盖。

ruby-on-rails mongodb mongoid
1个回答
0
投票

距您提出要求已经快两年了。到目前为止,还没有人回答这个问题,因为我想,答案将吸引潜在的反对者。我想知道是否还有任何答案与您相关。

我既不知道您在数据库中拥有什么,也不对MongoDB索引有很多了解,但是获胜的计划是“ FETCH”是一个好兆头。这意味着您的索引正在工作。相反,如果根本没有使用索引,则该计划将为“ COLLSCAN”。

现在解决其余问题:

  • 为什么要检查一些文件?
    • 至少,MongoDB需要检查它们返回的内容。 “ nReturned”和“ docsExamined”的值相同,表明MongoDB除了返回的文档外没有接触其他文档。
  • 为什么它不选择另一个计划而是拒绝它?
    • 我无法在此处查明确切原因,但是数据库确定使用它选择的索引在统计上更有效。它可能取决于存储在集合中的数据,例如在SQL实现中,即使表包含BTREE索引,有时计划者也会拒绝使用BTREE并扫描表,因为表仅包含一或几行数据。 BTREE查找只是不值得。MongoDB也可能发生类似的情况。而且唯一可以告诉您原因的人就是针对这种情况优化数据库的人。

这里的主要收获是:有时您需要放手。如果您希望每个细节都精确无误,那么您所取得的成就将不及那些无知的健康水平。如果数据库运行良好,则表明您运行良好。

© www.soinside.com 2019 - 2024. All rights reserved.