我目前正在使用MongoDB 3.6.1,并且我使用复合索引面临一种奇怪的行为。我有一个包含大约2百万个文档的集合,这是一个文档的结构:
{
"_id" : ObjectId("5af478b408e0f42946ff14ec"),
"IpStart" : NumberLong(50331649),
"IpEnd" : NumberLong(54525951),
"Latitude" : "47.634",
"Longitude" : "-122.342",
"PostalCode" : "98109",
"MarketId" : 308,
"City" : "Seattle",
"State" : "WA"
}
我的索引看起来像:
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "locations_fact.IpRangeLocations"
},
{
"v" : 2,
"unique" : true,
"key" : {
"IpStart" : 1,
"IpEnd" : 1
},
"name" : "IpStart_1_IpEnd_1",
"ns" : "locations_fact.IpRangeLocations"
}
]
当我提出这个问题时:
db.IpRangeLocations.find({"IpStart":{$lt:1209192009}, "IpEnd":{$gt:1209192009}}).explain("executionStats")
当我只检查955043键时,我得到了5秒的响应。
以下是解释选项启用的响应:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "locations_fact.IpRangeLocations",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"IpStart" : {
"$lt" : 1209192009.0
}
},
{
"IpEnd" : {
"$gt" : 1209192009.0
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"IpStart" : 1,
"IpEnd" : 1
},
"indexName" : "IpStart_1_IpEnd_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"IpStart" : [],
"IpEnd" : []
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"IpStart" : [
"[-inf.0, 1209192009.0)"
],
"IpEnd" : [
"(1209192009.0, inf.0]"
]
}
}
},
"rejectedPlans" : []
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 5019,
"totalKeysExamined" : 955043,
"totalDocsExamined" : 1,
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 1,
"executionTimeMillisEstimate" : 4823,
"works" : 955043,
"advanced" : 1,
"needTime" : 955041,
"needYield" : 0,
"saveState" : 7530,
"restoreState" : 7530,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 1,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1,
"executionTimeMillisEstimate" : 4773,
"works" : 955043,
"advanced" : 1,
"needTime" : 955041,
"needYield" : 0,
"saveState" : 7530,
"restoreState" : 7530,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"IpStart" : 1,
"IpEnd" : 1
},
"indexName" : "IpStart_1_IpEnd_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"IpStart" : [],
"IpEnd" : []
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"IpStart" : [
"[-inf.0, 1209192009.0)"
],
"IpEnd" : [
"(1209192009.0, inf.0]"
]
},
"keysExamined" : 955043,
"seeks" : 955042,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"serverInfo" : {
"host" : "SOLANOD",
"port" : 27017,
"version" : "3.6.1",
"gitVersion" : "025d4f4fe61efd1fb6f0005be20cb45a004093d1"
},
"ok" : 1.0
}
然后当我删除复合索引时,我得到了更好的结果(1.932秒),但它检查了1726879个文档(更多的是使用索引)
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "locations_fact.IpRangeLocations",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"IpStart" : {
"$lt" : 1209192009.0
}
},
{
"IpEnd" : {
"$gt" : 1209192009.0
}
}
]
},
"winningPlan" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"IpStart" : {
"$lt" : 1209192009.0
}
},
{
"IpEnd" : {
"$gt" : 1209192009.0
}
}
]
},
"direction" : "forward"
},
"rejectedPlans" : []
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 1916,
"totalKeysExamined" : 0,
"totalDocsExamined" : 1726879,
"executionStages" : {
"stage" : "COLLSCAN",
"filter" : {
"$and" : [
{
"IpStart" : {
"$lt" : 1209192009.0
}
},
{
"IpEnd" : {
"$gt" : 1209192009.0
}
}
]
},
"nReturned" : 1,
"executionTimeMillisEstimate" : 1697,
"works" : 1726881,
"advanced" : 1,
"needTime" : 1726879,
"needYield" : 0,
"saveState" : 13531,
"restoreState" : 13531,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 1726879
}
},
"serverInfo" : {
"host" : "SOLANOD",
"port" : 27017,
"version" : "3.6.1",
"gitVersion" : "025d4f4fe61efd1fb6f0005be20cb45a004093d1"
},
"ok" : 1.0
}
此外,我尝试创建单独的索引(一个用于IpStart,另一个用于IpEnd),但没有任何改进。
当我使用语句和复合索引对SQL进行相同的查询时:
SELECT * FROM [dbo].[ip_range_location] WHERE ipaddress BETWEEN ip_start AND ip_end
它不会超过300毫秒。
我期待Mongo会在100毫秒内回答,但我不确定这里会出现什么问题。
任何想法都很棒,谢谢