我正在对一个非常大的集合(500米文档)运行查询,有时会超时(6分钟)或需要很长时间(3-6分钟)。
我索引了所有相关字段(没有复合索引):Tag,trophies,battleLogMonitorFrequency,profileRefreshedAt被索引,totalIndexSize低于我的内存的60%(45gb索引大小为153gb,可用于mongodb)。
{
"v" : 2,
"key" : {
"battleLogMonitorFrequency" : 1,
"profileRefreshedAt" : 1,
"trophies" : -1
},
"name" : "battleLogMonitorFrequency_1_profileRefreshedAt_1_trophies_-1",
"ns" : "dbname.playerprofiles",
"background" : true
}
我的问题:
为什么在获得排序结果之前需要这么长时间(甚至超时)?我认为索引我排序和过滤的字段对于该查询应该足够了吗?
修改:完整查询说明结果:https://hastebin.com/ofixobasix.bash
编辑2: getIndexes()的输出:https://hastebin.com/azayojokez.scala
编辑3:在我的查询使用复合索引的建议之后,我注意到结果根本没有改变。查询仍然需要很长时间才能执行。请参阅以下说明结果:https://hastebin.com/ragixuqaci.bash
已添加此索引:
file.getName()
答案 0 :(得分:3)
您的下载方式如下:
您创建了复合索引{battleLogMonitorFrequency: 1, profileRefreshedAt: 1, trophies: -1}
,并在排序期间遇到内存不足问题。
errmsg: \"Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit.\""
我按照以下步骤解决了这个问题。
1。创建了100m记录的集合
db.myc.count() > 100034080
我的查询如下:
db.myc.find({field1 : 1, field2: {$lt : 800}}).sort({field3 : 1})
查询应该返回 38.9m记录(我知道这很大但我想加载测试)
<强> 2。然后我创建了索引{field1 : 1, field2: 1, field3:1}
执行查询并获得Out of Memory for Sort。在这里,我能够重现OP的问题。
(解释片段)
"executionStats" : {
"executionSuccess" : false,
"errorMessage" : "Exec error resulting in state FAILURE :: caused by :: errmsg: \"Sort operation used more than the maximum 33554432 bytes of RAM. Add an index, or specify a smaller limit.\"",
"errorCode" : 96,
"nReturned" : 0,
"executionTimeMillis" : 19033,
"totalKeysExamined" : 322639,
"totalDocsExamined" : 322639,
"executionStages" : {
"inputStage" : {
"inputStage" : {
"inputStage" : {
"indexName" : "field1_1_field2_1_field3_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"field1" : [ ],
"field2" : [ ],
"field3" : [ ]
},
}
}
}
}
}
第3。 (解决方案)更改了索引{field1 : 1, field3: 1, field2:1}
中字段的顺序
重新执行查询,这次我得到了我的回复。
此外,totalDocsExamined
和nReturned
实际上相同,这表明Mongo查询优化工具完美地使用了索引。
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 38901493,
"executionTimeMillis" : 1571781,
"totalKeysExamined" : 38902394,
"totalDocsExamined" : 38901493,
"executionStages" : {
"inputStage" : {
"indexName" : "field1_1_field3_1_field2_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"field1" : [ ],
"field3" : [ ],
"field2" : [ ]
},
}
}
}
虽然,我的查询花了很长时间才能执行,但这很明显,因为它返回(不切实际的)38.9m记录。我更关心的是mongo使用正确的索引吗?答案是肯定的。
<强>说明:强>
OP的查询是Mongo Equality, Range, Sort
问题的典型问卷。
当索引超过equality field - range field - sort field
Mongo时,仅将其用于过滤器而不用于排序。因此,排序在内存中执行。
为了解决这个问题,我们需要将范围字段保留在复合索引的末尾。
我找到了good article来更好地解释这个情景。
答案 1 :(得分:1)
根据explain()
输出,mongod
选择的索引为profileRefreshedAt_1
,并且需要扫描408295390
个键。这是整个系列的约82%。然后,这个大的结果集需要非索引匹配来满足battleLogMonitorFrequency
子句。这意味着,408295390
返回的每个IXSCAN
密钥需要由mongod
匹配,无需索引,将每个文档从磁盘拉出,进入缓存,以进行评估
查看集合中可用的索引,我建议在battleLogMonitorFrequency
和profileRefreshedAt
上创建复合索引。根据哪个字段的查询匹配较少,这应该在索引中排在第一位。例如,给定以下条件:
$eq
336有100,000场比赛$lt
新日期(“2018-04-29T00:00:00.000Z”)有100,000,000场比赛我们想要创建表单的索引
battleLogMonitorFrequency:1, profileRefreshedAt:1
因为battleLogMonitorFrequency
上的查询首先减少了我们在profileRefreshedAt
子句中扫描的密钥数量。
如果条件是相反的,那么我们将反转索引中的键排序。有关更多详细信息,请参阅https://docs.mongodb.com/manual/tutorial/create-queries-that-ensure-selectivity/index.html。