How to improve slow aggregate query in mongodb? - mongodb

I am querying for stock tickers that match the tickers.symbol array:
db.getCollection('reads').explain("executionStats").aggregate([
{ $match: { 'tickers.symbol': { $in: ['APPL'] } } },
{
$project: {
content: 0,
htm: 0,
},
},
{
$group: {
_id: '$url',
root: { $first: '$$ROOT' },
},
},
{ $sort: { 'root.createdAt': -1 } },
{ $limit: 50 },
{ $replaceRoot: { newRoot: '$root' } },
])
/* 1 */
{
"stages" : [
{
"$cursor" : {
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "briskreader.reads",
"indexFilterSet" : false,
"parsedQuery" : {
"tickers.symbol" : {
"$eq" : "APPL"
}
},
"queryHash" : "301E9992",
"planCacheKey" : "97D88657",
"winningPlan" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"tickers.symbol" : 1.0
},
"indexName" : "tickers.symbol_1",
"isMultiKey" : true,
"multiKeyPaths" : {
"tickers.symbol" : [
"tickers"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"tickers.symbol" : [
"[\"APPL\", \"APPL\"]"
]
}
}
},
"rejectedPlans" : [
{
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"tickers.symbol" : 1.0,
"createdAt" : -1.0
},
"indexName" : "tickers.symbol_1_createdAt_-1",
"isMultiKey" : true,
"multiKeyPaths" : {
"tickers.symbol" : [
"tickers"
],
"createdAt" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"tickers.symbol" : [
"[\"APPL\", \"APPL\"]"
],
"createdAt" : [
"[MaxKey, MinKey]"
]
}
}
}
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 0,
"executionTimeMillis" : 118,
"totalKeysExamined" : 0,
"totalDocsExamined" : 0,
"executionStages" : {
"stage" : "FETCH",
"nReturned" : 0,
"executionTimeMillisEstimate" : 64,
"works" : 2,
"advanced" : 0,
"needTime" : 0,
"needYield" : 0,
"saveState" : 3,
"restoreState" : 3,
"isEOF" : 1,
"docsExamined" : 0,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 0,
"executionTimeMillisEstimate" : 64,
"works" : 1,
"advanced" : 0,
"needTime" : 0,
"needYield" : 0,
"saveState" : 3,
"restoreState" : 3,
"isEOF" : 1,
"keyPattern" : {
"tickers.symbol" : 1.0
},
"indexName" : "tickers.symbol_1",
"isMultiKey" : true,
"multiKeyPaths" : {
"tickers.symbol" : [
"tickers"
]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"tickers.symbol" : [
"[\"APPL\", \"APPL\"]"
]
},
"keysExamined" : 0,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0
}
}
}
},
"nReturned" : NumberLong(0),
"executionTimeMillisEstimate" : NumberLong(0)
},
{
"$project" : {
"content" : false,
"htm" : false
},
"nReturned" : NumberLong(0),
"executionTimeMillisEstimate" : NumberLong(0)
},
{
"$group" : {
"_id" : "$url",
"root" : {
"$first" : "$$ROOT"
}
},
"nReturned" : NumberLong(0),
"executionTimeMillisEstimate" : NumberLong(0)
},
{
"$sort" : {
"sortKey" : {
"root.createdAt" : -1
},
"limit" : NumberLong(50)
},
"nReturned" : NumberLong(0),
"executionTimeMillisEstimate" : NumberLong(0)
},
{
"$replaceRoot" : {
"newRoot" : "$root"
},
"nReturned" : NumberLong(0),
"executionTimeMillisEstimate" : NumberLong(0)
}
],
"serverInfo" : {
"host" : "4da40b419852",
"port" : 27017,
"version" : "4.4.3",
"gitVersion" : "913d6b62acfbb344dde1b116f4161360acd8fd13"
},
"ok" : 1.0
}
Here is an example doc:
/* 2 */
{
"_id" : ObjectId("60073943ef54cc001c61ee7e"),
"summary" : {
"topics" : [
"brain",
"virus",
"mice",
"kumar",
"lungs",
"covid19",
"disease",
"symptoms",
"severe",
"illness"
],
"sentiment" : -0.0384615384615385,
"words" : 605,
"difficulty" : 0.766666666666667,
"minutes" : 5
},
"views" : 1,
"title" : "Study finds COVID-19 attack on brain, not lungs, triggers severe disease in mice: The findings have implications for understanding the wide range in symptoms and severity of illness among humans who are infected by SARS-CoV-2",
"byline" : null,
"dir" : null,
"length" : 3333,
"excerpt" : "Researchers have found that infecting the nasal passages of mice with the virus that causes COVID-19 led to a rapid, escalating attack on the brain that triggered severe illness, even after the lungs were successfully clearing themselves of the virus.",
"siteName" : "ScienceDaily",
"tickers" : [
{
"_id" : ObjectId("6007393aef54cc001c61ee7a"),
"name" : "Visa Inc.",
"symbol" : "V",
"exchange" : "nyse"
}
],
"cryptos" : [],
"meta" : {
"title" : "Study finds COVID-19 attack on brain, not lungs, triggers severe disease in mice: The findings have implications for understanding the wide range in symptoms and severity of illness among humans who are infected by SARS-CoV-2 -- ScienceDaily",
"thumb" : "https://www.sciencedaily.com/images/scidaily-icon.png"
},
"url" : "https://www.sciencedaily.com/releases/2021/01/210119114456.htm",
"host" : "www.sciencedaily.com",
"createdAt" : ISODate("2021-01-19T19:55:47.433Z"),
"updatedAt" : ISODate("2021-01-19T19:55:47.433Z"),
"shortId" : "9RVS9YmfYeY",
"__v" : 0
}
The query works with small collections (dev) but in prod with 100k docs it timesout after 300 seconds.

As long as there is an index on createdAt, moving sort before project will do.
move project after limit
db.getCollection('reads').explain("executionStats").aggregate([
{ $match: { 'tickers.symbol': {$in:['APPL']} } },
{ $sort: { 'createdAt': -1 } },
{
$group: {
_id: '$url',
root: { $first: '$$ROOT' },
},
},
{ $limit: 50 },
{
$project: {
"root.content": 0,
"root.htm": 0,
},
},
{ $replaceRoot: { newRoot: '$root' } },
])
$in
As it is written { 'tickers.symbol': {$in:['APPL'] }} should be written { 'tickers.symbol': 'APPL' }. But for many values, keep the original syntax.

Related

MongoDB 4.4 slow query

I execute a query command in mongos, but it executes for a long time.
mongos> db.message.find({"toid":123,"$or":[{"rid":2},{"validtime":null},{"fromid":2},{"validtime":{"$lt":1649565335}}]}).sort({sortid:-1}).limit(10)
slow query log like this:
{
"op" : "command",
"ns" : "sns.message",
"command" : {
"explain" : {
"find" : "message",
"filter" : {
"toid" : 123,
"$or" : [
{
"rid" : 2
},
{
"validtime" : null
},
{
"fromid" : 2
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
"limit" : 10,
"singleBatch" : true,
"sort" : {
"sortid" : -1
},
"options" : {
}
},
"verbosity" : "executionStats",
"$db" : "sns"
},
"numYield" : 6477,
"locks" : {
"ReplicationStateTransition" : {
"acquireCount" : {
"w" : NumberLong(6479)
}
},
"Global" : {
"acquireCount" : {
"r" : NumberLong(6479)
}
},
"Database" : {
"acquireCount" : {
"r" : NumberLong(6478)
}
},
"Collection" : {
"acquireCount" : {
"r" : NumberLong(6478)
}
},
"Mutex" : {
"acquireCount" : {
"r" : NumberLong(1)
}
}
},
"flowControl" : {
},
"storage" : {
"data" : {
"bytesRead" : NumberLong(91629495),
"timeReadingMicros" : NumberLong(68943)
}
},
"responseLength" : 5159,
"protocol" : "op_query",
"millis" : 13477,
"ts" : ISODate("2022-04-11T05:46:33.221Z"),
"client" : "127.0.0.1",
"allUsers" : [ ],
"user" : ""
}
and the explain information is as follows:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "sns.message",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
{
"toid" : {
"$eq" : 123
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 10,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
{
"toid" : {
"$eq" : 123
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"sortid" : 1,
"toid" : 1
},
"indexName" : "toid_1_sortid_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"sortid" : [ ],
"toid" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "backward",
"indexBounds" : {
"sortid" : [
"[MaxKey, MinKey]"
],
"toid" : [
"[MaxKey, MinKey]"
]
}
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"sortid" : -1
},
"memLimit" : 104857600,
"limitAmount" : 10,
"type" : "simple",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"toid" : 1
},
"indexName" : "toid_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"toid" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"toid" : [
"[123.0, 123.0]"
]
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"sortid" : -1
},
"memLimit" : 104857600,
"limitAmount" : 10,
"type" : "simple",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"toid" : 1,
"replyid" : 1
},
"indexName" : "toid_1_replyid_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"toid" : [ ],
"replyid" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"toid" : [
"[123.0, 123.0]"
],
"replyid" : [
"[MinKey, MaxKey]"
]
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"sortid" : -1
},
"memLimit" : 104857600,
"limitAmount" : 10,
"type" : "simple",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"toid" : 1,
"validtime" : 1,
"time" : 1
},
"indexName" : "toid_1_validtime_1_time_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"toid" : [ ],
"validtime" : [ ],
"time" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"toid" : [
"[123.0, 123.0]"
],
"validtime" : [
"[MinKey, MaxKey]"
],
"time" : [
"[MinKey, MaxKey]"
]
}
}
}
},
// omit...
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 10,
"executionTimeMillis" : 13477,
"totalKeysExamined" : 1295456,
"totalDocsExamined" : 1295456,
"executionStages" : {
"stage" : "LIMIT",
"nReturned" : 10,
"executionTimeMillisEstimate" : 700,
"works" : 1295457,
"advanced" : 10,
"needTime" : 1295446,
"needYield" : 0,
"saveState" : 6477,
"restoreState" : 6477,
"isEOF" : 1,
"limitAmount" : 10,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$or" : [
{
"fromid" : {
"$eq" : 2
}
},
{
"rid" : {
"$eq" : 2
}
},
{
"validtime" : {
"$eq" : null
}
},
{
"validtime" : {
"$lt" : 1649565335
}
}
]
},
{
"toid" : {
"$eq" : 123
}
}
]
},
"nReturned" : 10,
"executionTimeMillisEstimate" : 690,
"works" : 1295456,
"advanced" : 10,
"needTime" : 1295446,
"needYield" : 0,
"saveState" : 6477,
"restoreState" : 6477,
"isEOF" : 0,
"docsExamined" : 1295456,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 1295456,
"executionTimeMillisEstimate" : 188,
"works" : 1295456,
"advanced" : 1295456,
"needTime" : 0,
"needYield" : 0,
"saveState" : 6477,
"restoreState" : 6477,
"isEOF" : 0,
"keyPattern" : {
"sortid" : 1,
"toid" : 1
},
"indexName" : "toid_1_sortid_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"sortid" : [ ],
"toid" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "backward",
"indexBounds" : {
"sortid" : [
"[MaxKey, MinKey]"
],
"toid" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 1295456,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0
}
}
}
},
"serverInfo" : {
"host" : "MongoDB",
"port" : 22005,
"version" : "4.4.13",
"gitVersion" : "df25c71b8674a78e17468f48bcda5285decb9246"
},
"ok" : 1,
"$gleStats" : {
"lastOpTime" : Timestamp(0, 0),
"electionId" : ObjectId("7fffffff0000000000000001")
},
"lastCommittedOpTime" : Timestamp(1649655993, 20),
"$configServerState" : {
"opTime" : {
"ts" : Timestamp(1649655992, 11),
"t" : NumberLong(1)
}
},
"$clusterTime" : {
"clusterTime" : Timestamp(1649655993, 26),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1649655993, 26)
}
in the "executionStats" block, both the "LIMIT" stage and its substage take less than or equal to 700ms, but the total time is 13477ms. what happened during this time? How can I locate the reason for the time consuming? And Looking forward to optimization suggestions.
Thanks
The total executionTimeMillis is the "total" amount of time required for both planning + execution, this means there are certain factors now calculated in the breakdown per stage.
For example:
Lock acquisition / overall db load.
When planning only a 100 document sample is examined, however as you can see for the actual query over 1milion documents are being fetched. If the there are other operations on the same collection that lock it or if the db is in general under heavy load this can affect performance as your query is waiting in line for resources.
Disk latency
As you can see your docsExamined is equal to keysExamined, this means that every single documents that matched the query needed to be fetched from disk to be examined.
This is quite a lot of overhead. and I suspect this is the main "issue" regarding performance. You could probably lower this by creating a proper compound index to satisfy this query.
You can also add a disk with more IOPS, boosting hardware will always increase performance, especially if it's at the bottleneck places.

MongoDB index doing a collection scan instead index scan [duplicate]

Here are the compound index and single index I have for this Collection:
///db.Collection.getIndexes()
/* 1 */
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "service.Collection"
},
/* 2 */
{
"v" : 2,
"key" : {
"FirstId" : 1,
"SecondId" : 1,
"CreationTime" : -1
},
"name" : "FirstIdSecondIdCreationTime",
"collation" : {
"locale" : "en",
"caseLevel" : false,
"caseFirst" : "off",
"strength" : 1,
"numericOrdering" : false,
"alternate" : "non-ignorable",
"maxVariable" : "punct",
"normalization" : false,
"backwards" : false,
"version" : "57.1"
},
"ns" : "service.Collection"
},
/* 3 */
{
"v" : 2,
"key" : {
"CreationTime" : 1
},
"name" : "CreationTime",
"collation" : {
"locale" : "en",
"caseLevel" : false,
"caseFirst" : "off",
"strength" : 1,
"numericOrdering" : false,
"alternate" : "non-ignorable",
"maxVariable" : "punct",
"normalization" : false,
"backwards" : false,
"version" : "57.1"
},
"ns" : "service.Collection"
}
The expected result is an IXSCAN using the FirstIdSecondIdCreationTime index:
///service.Collection.find({ FirstId: "771367b7-4bef-49ab-bda1-6230254c6349", ///SecondId: "3bffb3cd-fb5e-43e5-abd1-e0b48c97f78f" })
/// .projection({})
/// .sort({_id:-1}).hint("FirstIdSecondIdCreationTime").explain('executionStats')
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "service.Collection",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"FirstId" : {
"$eq" : "771367b7-4bef-49ab-bda1-6230254c6349"
}
},
{
"SecondId" : {
"$eq" : "3bffb3cd-fb5e-43e5-abd1-e0b48c97f78f"
}
}
]
},
"winningPlan" : {
"stage" : "SORT",
"sortPattern" : {
"_id" : -1
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"FirstId" : {
"$eq" : "771367b7-4bef-49ab-bda1-6230254c6349"
}
},
{
"SecondId" : {
"$eq" : "3bffb3cd-fb5e-43e5-abd1-e0b48c97f78f"
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"FirstId" : 1,
"SecondId" : 1,
"CreationTime" : -1
},
"indexName" : "FirstIdSecondIdCreationTime",
"collation" : {
"locale" : "en",
"caseLevel" : false,
"caseFirst" : "off",
"strength" : 1,
"numericOrdering" : false,
"alternate" : "non-ignorable",
"maxVariable" : "punct",
"normalization" : false,
"backwards" : false,
"version" : "57.1"
},
"isMultiKey" : false,
"multiKeyPaths" : {
"FirstId" : [ ],
"SecondId" : [ ],
"CreationTime" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"FirstId" : [
"[MinKey, MaxKey]"
],
"SecondId" : [
"[MinKey, MaxKey]"
],
"CreationTime" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 5491,
"totalKeysExamined" : 856730,
"totalDocsExamined" : 856730,
"executionStages" : {
"stage" : "SORT",
"nReturned" : 1,
"executionTimeMillisEstimate" : 5261,
"works" : 856734,
"advanced" : 1,
"needTime" : 856732,
"needYield" : 0,
"saveState" : 6697,
"restoreState" : 6697,
"isEOF" : 1,
"invalidates" : 0,
"sortPattern" : {
"_id" : -1
},
"memUsage" : 432,
"memLimit" : 33554432,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"nReturned" : 1,
"executionTimeMillisEstimate" : 5201,
"works" : 856732,
"advanced" : 1,
"needTime" : 856730,
"needYield" : 0,
"saveState" : 6697,
"restoreState" : 6697,
"isEOF" : 1,
"invalidates" : 0,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"FirstId" : {
"$eq" : "771367b7-4bef-49ab-bda1-6230254c6349"
}
},
{
"SecondId" : {
"$eq" : "3bffb3cd-fb5e-43e5-abd1-e0b48c97f78f"
}
}
]
},
"nReturned" : 1,
"executionTimeMillisEstimate" : 5131,
"works" : 856731,
"advanced" : 1,
"needTime" : 856729,
"needYield" : 0,
"saveState" : 6697,
"restoreState" : 6697,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 856730,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 856730,
"executionTimeMillisEstimate" : 820,
"works" : 856731,
"advanced" : 856730,
"needTime" : 0,
"needYield" : 0,
"saveState" : 6697,
"restoreState" : 6697,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"FirstId" : 1,
"SecondId" : 1,
"CreationTime" : -1
},
"indexName" : "FirstIdSecondIdCreationTime",
"collation" : {
"locale" : "en",
"caseLevel" : false,
"caseFirst" : "off",
"strength" : 1,
"numericOrdering" : false,
"alternate" : "non-ignorable",
"maxVariable" : "punct",
"normalization" : false,
"backwards" : false,
"version" : "57.1"
},
"isMultiKey" : false,
"multiKeyPaths" : {
"FirstId" : [ ],
"SecondId" : [ ],
"CreationTime" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"FirstId" : [
"[MinKey, MaxKey]"
],
"SecondId" : [
"[MinKey, MaxKey]"
],
"CreationTime" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 856730,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0,
"indexDef" : {
"indexName" : "FirstIdSecondIdCreationTime",
"isMultiKey" : false,
"multiKeyPaths" : {
"FirstId" : [ ],
"SecondId" : [ ],
"CreationTime" : [ ]
},
"keyPattern" : {
"FirstId" : 1,
"SecondId" : 1,
"CreationTime" : -1
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"direction" : "forward"
}
}
}
}
}
but the actual result is a COLLSCAN that takes over 8000ms:
"event": {
"dataset": "mongodb.log",
"module": "mongodb"
},
"service": {
"type": "mongodb"
},
"message": "command service.Collection command: find { find: \"Collection\",
filter: { FirstId: \"771367b7-4bef-49ab-bda1-6230254c6349\", SecondId: \"3bffb3cd-fb5e-43e5-abd1-e0b48c97f78f\" }, sort: { CreationTime: -1 }, limit: 1,
planSummary: COLLSCAN keysExamined:0 docsExamined:784787 hasSortStage:1 cursorExhausted:1 numYields:6175 nreturned:1 reslen:677
locks:{ Global: { acquireCount: { r: 12352 } }, Database: { acquireCount: { r: 6176 } }, Collection: { acquireCount: { r: 6176 } } } protocol:op_msg 8441ms",
"mongodb.docsExamined": 784787,
"fileset": {
"name": "log"
},
Why am I COLLSCANing instead of IXSCANing with the FirstIdSecondIDCreationTime compound index? Is there a way to change my index/ my query to speed up the query?
Per a suggestion in the comments, I've run explain("allPlansExecution").
///db.Collection.find({ FirstId: "771367b7-4bef-49ab-bda1-6230254c6349", ///SecondId: "3bffb3cd-fb5e-43e5-abd1-e0b48c97f78f" })
/// .projection({})
/// .sort({_id:-1}).explain('allPlansExecution')
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "service.Collection",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"FirstId" : {
"$eq" : "771367b7-4bef-49ab-bda1-6230254c6349"
}
},
{
"SecondId" : {
"$eq" : "3bffb3cd-fb5e-43e5-abd1-e0b48c97f78f"
}
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"FirstId" : {
"$eq" : "771367b7-4bef-49ab-bda1-6230254c6349"
}
},
{
"SecondId" : {
"$eq" : "3bffb3cd-fb5e-43e5-abd1-e0b48c97f78f"
}
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_id" : 1
},
"indexName" : "_id_",
"isMultiKey" : false,
"multiKeyPaths" : {
"_id" : [ ]
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "backward",
"indexBounds" : {
"_id" : [
"[MaxKey, MinKey]"
]
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 1,
"executionTimeMillis" : 5408,
"totalKeysExamined" : 856748,
"totalDocsExamined" : 856748,
"executionStages" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"FirstId" : {
"$eq" : "771367b7-4bef-49ab-bda1-6230254c6349"
}
},
{
"SecondId" : {
"$eq" : "3bffb3cd-fb5e-43e5-abd1-e0b48c97f78f"
}
}
]
},
"nReturned" : 1,
"executionTimeMillisEstimate" : 4862,
"works" : 856749,
"advanced" : 1,
"needTime" : 856747,
"needYield" : 0,
"saveState" : 6694,
"restoreState" : 6694,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 856748,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 856748,
"executionTimeMillisEstimate" : 1220,
"works" : 856749,
"advanced" : 856748,
"needTime" : 0,
"needYield" : 0,
"saveState" : 6694,
"restoreState" : 6694,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"_id" : 1
},
"indexName" : "_id_",
"isMultiKey" : false,
"multiKeyPaths" : {
"_id" : [ ]
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "backward",
"indexBounds" : {
"_id" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 856748,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
},
"allPlansExecution" : [ ]
}
}
The "FirstIdSecondIdCreationTime" index was not automatically considered because it was created with a collation, and the query is being run without a collation.
Use the .collation() cursor method to specify the same collation for the query that was used for the index.
The 5.5 second run time using that index is pretty slow as well.
You may see some improvement in that query if you create an index on {FirstId: 1, SecondId: 1, _id: 1} so that they query executor can use the index to meet the sort instead of an in-memory sort.
Can you please sort by leading indexes i.e firstId, secondId and creationTime in the same sequence and see index is used. it will give an idea whether leading indexes fields should be there in sort as well.

a mongodb shard shows strange slow behavior

I'm testing below aggregate
db.getCollection('token_transfer').explain("executionStats").aggregate([
{$match: {
$and: [{"contract_address":"0xTTTTTTTTTTT"}, {"timestamp": {$gte: ISODate("2019-12-13T00:45:05.000Z")}}]
}},
{$group: {
_id: null,
transfer_count: {$sum: 1},
transfer_volume_sum: {$sum: "$value"},
}}
])
indexes of the token_transfer collection is like below
{
"contract_address" : 1.0,
"value" : -1.0,
"timestamp" : 1.0
},
{
"tx_hash" : "hashed"
}
...
token_transfer collection has below fields
[contract_address, from, to, value, tx_hash, timestamp, ...]
shard key is like below
shard key: { "tx_hash" : "hashed" }
Key point is that it tooks 42706 executionTimeMillis at only shd2.
The other shards show under 5000 executionTimeMillis.
Why does slowness occur on certain shard which results in total query slow?
Below is my executionStats of aggregate.
/* 1 */
{
"mergeType" : "mongos",
"splitPipeline" : {
"shardsPart" : [
{
"$match" : {
"$and" : [
{
"contract_address" : {
"$eq" : "0xTTTTTTTTTTT"
}
},
{
"timestamp" : {
"$gte" : ISODate("2019-12-13T00:45:05.000Z")
}
}
]
}
},
{
"$group" : {
"_id" : {
"$const" : null
},
"transfer_count" : {
"$sum" : {
"$const" : 1.0
}
},
"transfer_volume_sum" : {
"$sum" : "$value"
}
}
}
],
"mergerPart" : [
{
"$group" : {
"_id" : "$$ROOT._id",
"transfer_count" : {
"$sum" : "$$ROOT.transfer_count"
},
"transfer_volume_sum" : {
"$sum" : "$$ROOT.transfer_volume_sum"
},
"$doingMerge" : true
}
}
]
},
"shards" : {
"shd3" : {
"host" : "x-db1:33333",
"stages" : [
{
"$cursor" : {
"query" : {
"$and" : [
{
"contract_address" : "0xTTTTTTTTTTT"
},
{
"timestamp" : {
"$gte" : ISODate("2019-12-13T00:45:05.000Z")
}
}
]
},
"fields" : {
"value" : 1,
"_id" : 0
},
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.token_transfer",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"contract_address" : {
"$eq" : "0xTTTTTTTTTTT"
}
},
{
"timestamp" : {
"$gte" : ISODate("2019-12-13T00:45:05.000Z")
}
}
]
},
"winningPlan" : {
"stage" : "PROJECTION",
"transformBy" : {
"value" : 1,
"_id" : 0
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"contract_address" : 1.0,
"value" : -1.0,
"timestamp" : 1.0
},
"indexName" : "contract_address_1_value_-1_timestamp_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"contract_address" : [],
"value" : [],
"timestamp" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"contract_address" : [
"[\"0xTTTTTTTTTTT\", \"0xTTTTTTTTTTT\"]"
],
"value" : [
"[MaxKey, MinKey]"
],
"timestamp" : [
"[new Date(1576197905000), new Date(9223372036854775807)]"
]
}
}
},
"rejectedPlans" : []
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 671,
"executionTimeMillis" : 1384,
"totalKeysExamined" : 1231507,
"totalDocsExamined" : 0,
"executionStages" : {
"stage" : "PROJECTION",
"nReturned" : 671,
"executionTimeMillisEstimate" : 1370,
"works" : 1231507,
"advanced" : 671,
"needTime" : 1230835,
"needYield" : 0,
"saveState" : 9622,
"restoreState" : 9622,
"isEOF" : 1,
"invalidates" : 0,
"transformBy" : {
"value" : 1,
"_id" : 0
},
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 671,
"executionTimeMillisEstimate" : 1340,
"works" : 1231507,
"advanced" : 671,
"needTime" : 1230835,
"needYield" : 0,
"saveState" : 9622,
"restoreState" : 9622,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"contract_address" : 1.0,
"value" : -1.0,
"timestamp" : 1.0
},
"indexName" : "contract_address_1_value_-1_timestamp_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"contract_address" : [],
"value" : [],
"timestamp" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"contract_address" : [
"[\"0xTTTTTTTTTTT\", \"0xTTTTTTTTTTT\"]"
],
"value" : [
"[MaxKey, MinKey]"
],
"timestamp" : [
"[new Date(1576197905000), new Date(9223372036854775807)]"
]
},
"keysExamined" : 1231507,
"seeks" : 1230836,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
}
}
},
{
"$group" : {
"_id" : {
"$const" : null
},
"transfer_count" : {
"$sum" : {
"$const" : 1.0
}
},
"transfer_volume_sum" : {
"$sum" : "$value"
}
}
}
]
},
"shd1" : {
"host" : "x-db2:11111",
"stages" : [
{
"$cursor" : {
"query" : {
"$and" : [
{
"contract_address" : "0xTTTTTTTTTTT"
},
{
"timestamp" : {
"$gte" : ISODate("2019-12-13T00:45:05.000Z")
}
}
]
},
"fields" : {
"value" : 1,
"_id" : 0
},
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.token_transfer",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"contract_address" : {
"$eq" : "0xTTTTTTTTTTT"
}
},
{
"timestamp" : {
"$gte" : ISODate("2019-12-13T00:45:05.000Z")
}
}
]
},
"winningPlan" : {
"stage" : "PROJECTION",
"transformBy" : {
"value" : 1,
"_id" : 0
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"contract_address" : 1.0,
"value" : -1.0,
"timestamp" : 1.0
},
"indexName" : "contract_address_1_value_-1_timestamp_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"contract_address" : [],
"value" : [],
"timestamp" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"contract_address" : [
"[\"0xTTTTTTTTTTT\", \"0xTTTTTTTTTTT\"]"
],
"value" : [
"[MaxKey, MinKey]"
],
"timestamp" : [
"[new Date(1576197905000), new Date(9223372036854775807)]"
]
}
}
},
"rejectedPlans" : []
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 680,
"executionTimeMillis" : 4545,
"totalKeysExamined" : 1234518,
"totalDocsExamined" : 0,
"executionStages" : {
"stage" : "PROJECTION",
"nReturned" : 680,
"executionTimeMillisEstimate" : 3685,
"works" : 1234518,
"advanced" : 680,
"needTime" : 1233837,
"needYield" : 0,
"saveState" : 9656,
"restoreState" : 9656,
"isEOF" : 1,
"invalidates" : 0,
"transformBy" : {
"value" : 1,
"_id" : 0
},
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 680,
"executionTimeMillisEstimate" : 3685,
"works" : 1234518,
"advanced" : 680,
"needTime" : 1233837,
"needYield" : 0,
"saveState" : 9656,
"restoreState" : 9656,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"contract_address" : 1.0,
"value" : -1.0,
"timestamp" : 1.0
},
"indexName" : "contract_address_1_value_-1_timestamp_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"contract_address" : [],
"value" : [],
"timestamp" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"contract_address" : [
"[\"0xTTTTTTTTTTT\", \"0xTTTTTTTTTTT\"]"
],
"value" : [
"[MaxKey, MinKey]"
],
"timestamp" : [
"[new Date(1576197905000), new Date(9223372036854775807)]"
]
},
"keysExamined" : 1234518,
"seeks" : 1233838,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
}
}
},
{
"$group" : {
"_id" : {
"$const" : null
},
"transfer_count" : {
"$sum" : {
"$const" : 1.0
}
},
"transfer_volume_sum" : {
"$sum" : "$value"
}
}
}
]
},
"shd2" : {
"host" : "x-db3:22222",
"stages" : [
{
"$cursor" : {
"query" : {
"$and" : [
{
"contract_address" : "0xTTTTTTTTTTT"
},
{
"timestamp" : {
"$gte" : ISODate("2019-12-13T00:45:05.000Z")
}
}
]
},
"fields" : {
"value" : 1,
"_id" : 0
},
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "test.token_transfer",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"contract_address" : {
"$eq" : "0xTTTTTTTTTTT"
}
},
{
"timestamp" : {
"$gte" : ISODate("2019-12-13T00:45:05.000Z")
}
}
]
},
"winningPlan" : {
"stage" : "PROJECTION",
"transformBy" : {
"value" : 1,
"_id" : 0
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"contract_address" : 1.0,
"value" : -1.0,
"timestamp" : 1.0
},
"indexName" : "contract_address_1_value_-1_timestamp_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"contract_address" : [],
"value" : [],
"timestamp" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"contract_address" : [
"[\"0xTTTTTTTTTTT\", \"0xTTTTTTTTTTT\"]"
],
"value" : [
"[MaxKey, MinKey]"
],
"timestamp" : [
"[new Date(1576197905000), new Date(9223372036854775807)]"
]
}
}
},
"rejectedPlans" : []
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 720,
"executionTimeMillis" : 42706,
"totalKeysExamined" : 1181569,
"totalDocsExamined" : 0,
"executionStages" : {
"stage" : "PROJECTION",
"nReturned" : 720,
"executionTimeMillisEstimate" : 4284,
"works" : 1181569,
"advanced" : 720,
"needTime" : 1180848,
"needYield" : 0,
"saveState" : 9249,
"restoreState" : 9249,
"isEOF" : 1,
"invalidates" : 0,
"transformBy" : {
"value" : 1,
"_id" : 0
},
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 720,
"executionTimeMillisEstimate" : 4244,
"works" : 1181569,
"advanced" : 720,
"needTime" : 1180848,
"needYield" : 0,
"saveState" : 9249,
"restoreState" : 9249,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"contract_address" : 1.0,
"value" : -1.0,
"timestamp" : 1.0
},
"indexName" : "contract_address_1_value_-1_timestamp_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"contract_address" : [],
"value" : [],
"timestamp" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"contract_address" : [
"[\"0xTTTTTTTTTTT\", \"0xTTTTTTTTTTT\"]"
],
"value" : [
"[MaxKey, MinKey]"
],
"timestamp" : [
"[new Date(1576197905000), new Date(9223372036854775807)]"
]
},
"keysExamined" : 1181569,
"seeks" : 1180849,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
}
}
},
{
"$group" : {
"_id" : {
"$const" : null
},
"transfer_count" : {
"$sum" : {
"$const" : 1.0
}
},
"transfer_volume_sum" : {
"$sum" : "$value"
}
}
}
]
}
},
"ok" : 1.0,
"$clusterTime" : {
"clusterTime" : Timestamp(1576201630, 116),
"signature" : {
"hash" : { "$binary" : "AAAAAAAAAAAAAAAAAAAAAAAAAAA=", "$type" : "00" },
"keyId" : NumberLong(0)
}
},
"operationTime" : Timestamp(1576201630, 115)
}

MongoDB - Index not being used when sorting and limiting on ranged query

I'm trying to get a sorted list of items using a ranged query on a collection containing bulletin-board data. The data structure of a "thread" document is:
{
"_id" : ObjectId("5a779b47f4fa72412126526a"),
"title" : "necessitatibus tincidunt libris assueverit",
"content" : "Corrumpitvenenatis cubilia adipiscing sollicitudin",
"flagged" : false,
"locked" : false,
"sticky" : false,
"lastPostAt" : ISODate("2018-02-05T06:35:24.656Z"),
"postCount" : 42,
"user" : ObjectId("5a779b46f4fa72412126525a"),
"category" : ObjectId("5a779b31f4fa724121265164"),
"createdAt" : ISODate("2018-02-04T23:46:15.852Z"),
"updatedAt" : ISODate("2018-02-05T06:35:24.656Z")
}
The query is:
db.threads.find({
category: ObjectId('5a779b31f4fa724121265142'),
_id : { $gt: ObjectId('5a779b5cf4fa724121269be8') }
}).sort({ sticky: -1, lastPostAt: -1, _id: 1 }).limit(25)
I set up the following indexes to support it:
{ category: 1, _id: 1 }
{ category: 1, _id: 1, sticky: 1, lastPostAt: 1 }
{ sticky: 1, lastPostAt: 1, _id: 1 }
In spite of this, it's still scanning hundreds of documents/keys according to execution stats:
{
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 772,
"executionTimeMillis" : 17,
"totalKeysExamined" : 772,
"totalDocsExamined" : 772,
"executionStages" : {
"stage" : "SORT",
"nReturned" : 772,
"executionTimeMillisEstimate" : 0,
"works" : 1547,
"advanced" : 772,
"needTime" : 774,
"needYield" : 0,
"saveState" : 33,
"restoreState" : 33,
"isEOF" : 1,
"invalidates" : 0,
"sortPattern" : {
"sticky" : -1,
"lastPostAt" : -1,
"_id" : 1
},
"memUsage" : 1482601,
"memLimit" : 33554432,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"nReturned" : 772,
"executionTimeMillisEstimate" : 0,
"works" : 774,
"advanced" : 772,
"needTime" : 1,
"needYield" : 0,
"saveState" : 33,
"restoreState" : 33,
"isEOF" : 1,
"invalidates" : 0,
"inputStage" : {
"stage" : "FETCH",
"nReturned" : 772,
"executionTimeMillisEstimate" : 0,
"works" : 773,
"advanced" : 772,
"needTime" : 0,
"needYield" : 0,
"saveState" : 33,
"restoreState" : 33,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 772,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 772,
"executionTimeMillisEstimate" : 0,
"works" : 773,
"advanced" : 772,
"needTime" : 0,
"needYield" : 0,
"saveState" : 33,
"restoreState" : 33,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"category" : 1,
"_id" : 1,
"sticky" : 1,
"lastPostAt" : 1
},
"indexName" : "category_1__id_1_sticky_1_lastPostAt_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"category" : [ ],
"_id" : [ ],
"sticky" : [ ],
"lastPostAt" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"category" : [
"[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
],
"_id" : [
"(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
],
"sticky" : [
"[MinKey, MaxKey]"
],
"lastPostAt" : [
"[MinKey, MaxKey]"
]
},
"keysExamined" : 772,
"seeks" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
}
}
}
When I take out the sorting stage, it correctly scans only 25 documents. And the keys examined (772) remains the same no matter which fields I place in the sort function.
Here is the full explain() for the sorted query:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "database.threads",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"category" : {
"$eq" : ObjectId("5a779b31f4fa724121265142")
}
},
{
"_id" : {
"$gt" : ObjectId("5a779b5cf4fa724121269be8")
}
}
]
},
"winningPlan" : {
"stage" : "SORT",
"sortPattern" : {
"sticky" : -1,
"lastPostAt" : -1,
"_id" : 1
},
"limitAmount" : 25,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"category" : 1,
"_id" : 1,
"sticky" : 1,
"lastPostAt" : 1
},
"indexName" : "category_1__id_1_sticky_1_lastPostAt_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"category" : [ ],
"_id" : [ ],
"sticky" : [ ],
"lastPostAt" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"category" : [
"[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
],
"_id" : [
"(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
],
"sticky" : [
"[MinKey, MaxKey]"
],
"lastPostAt" : [
"[MinKey, MaxKey]"
]
}
}
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"sticky" : -1,
"lastPostAt" : -1,
"_id" : 1
},
"limitAmount" : 25,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"_id" : {
"$gt" : ObjectId("5a779b5cf4fa724121269be8")
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"category" : 1
},
"indexName" : "category_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"category" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"category" : [
"[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"sticky" : -1,
"lastPostAt" : -1,
"_id" : 1
},
"limitAmount" : 25,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"category" : 1,
"_id" : 1
},
"indexName" : "category_1__id_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"category" : [ ],
"_id" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"category" : [
"[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
],
"_id" : [
"(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"sticky" : -1,
"lastPostAt" : -1,
"_id" : 1
},
"limitAmount" : 25,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"category" : {
"$eq" : ObjectId("5a779b31f4fa724121265142")
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_id" : 1
},
"indexName" : "_id_",
"isMultiKey" : false,
"multiKeyPaths" : {
"_id" : [ ]
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"_id" : [
"(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
]
}
}
}
}
}
]
},
"serverInfo" : {
"host" : "CRF-MBP.local",
"port" : 27017,
"version" : "3.6.2",
"gitVersion" : "489d177dbd0f0420a8ca04d39fd78d0a2c539420"
},
"ok" : 1
}
And here is the full explain() for the non-sorted query:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "database.threads",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"category" : {
"$eq" : ObjectId("5a779b31f4fa724121265142")
}
},
{
"_id" : {
"$gt" : ObjectId("5a779b5cf4fa724121269be8")
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 25,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"category" : 1,
"_id" : 1,
"sticky" : 1,
"lastPostAt" : 1
},
"indexName" : "category_1__id_1_sticky_1_lastPostAt_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"category" : [ ],
"_id" : [ ],
"sticky" : [ ],
"lastPostAt" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"category" : [
"[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
],
"_id" : [
"(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
],
"sticky" : [
"[MinKey, MaxKey]"
],
"lastPostAt" : [
"[MinKey, MaxKey]"
]
}
}
}
},
"rejectedPlans" : [
{
"stage" : "LIMIT",
"limitAmount" : 25,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"_id" : {
"$gt" : ObjectId("5a779b5cf4fa724121269be8")
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"category" : 1
},
"indexName" : "category_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"category" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"category" : [
"[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
]
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 25,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"category" : 1,
"_id" : 1
},
"indexName" : "category_1__id_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"category" : [ ],
"_id" : [ ]
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"category" : [
"[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
],
"_id" : [
"(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
]
}
}
}
},
{
"stage" : "LIMIT",
"limitAmount" : 25,
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"category" : {
"$eq" : ObjectId("5a779b31f4fa724121265142")
}
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"_id" : 1
},
"indexName" : "_id_",
"isMultiKey" : false,
"multiKeyPaths" : {
"_id" : [ ]
},
"isUnique" : true,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"_id" : [
"(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
]
}
}
}
}
]
},
"serverInfo" : {
"host" : "CRF-MBP.local",
"port" : 27017,
"version" : "3.6.2",
"gitVersion" : "489d177dbd0f0420a8ca04d39fd78d0a2c539420"
},
"ok" : 1
}
Does anyone have any idea why this might not fully use an index?
The issue is that none of your indexes actually help with the sorted query. This is the reason for the high number of scanned objects and the presence of SORT_KEY_GENERATOR stage (in-memory sort, limited to 32MB).
The non-sorted query, on the other hand, can use either the { category: 1, _id: 1 } or { category: 1, _id: 1, sticky: 1, lastPostAt: 1 } indexes. Note that it's perfectly valid to use either one, since one contains the prefix of the other. See Prefixes for more details.
MongoDB find() queries typically uses only one index, so a single compound index should cater for all the parameters of your query. This would include both the parameters of find() and sort().
A good writeup of how your index should be created is available in Optimizing MongoDB Compound Indexes. Let's take the main point of the article, where the compound index ordering should be equality --> sort --> range:
Your query "shape" is:
db.collection.find({category:..., _id: {$gt:...}})
.sort({sticky:-1, lastPostAt:-1, _id:1})
.limit(25)
We see that:
category:... is equality
sticky:-1, lastPostAt:-1, _id:1 is sort
_id: {$gt:...} is range
So the compound index you need is:
{category:1, sticky:-1, lastPostAt:-1, _id:1}
Where the winning plan of the explain() output of your query with the above index shows:
"winningPlan": {
"stage": "LIMIT",
"limitAmount": 25,
"inputStage": {
"stage": "FETCH",
"inputStage": {
"stage": "IXSCAN",
"keyPattern": {
"category": 1,
"sticky": -1,
"lastPostAt": -1,
"_id": 1
},
"indexName": "category_1_sticky_-1_lastPostAt_-1__id_1",
"isMultiKey": false,
"multiKeyPaths": {
"category": [ ],
"sticky": [ ],
"lastPostAt": [ ],
"_id": [ ]
},
"isUnique": false,
"isSparse": false,
"isPartial": false,
"indexVersion": 2,
"direction": "forward",
"indexBounds": {
"category": [
"[ObjectId('5a779b31f4fa724121265142'), ObjectId('5a779b31f4fa724121265142')]"
],
"sticky": [
"[MaxKey, MinKey]"
],
"lastPostAt": [
"[MaxKey, MinKey]"
],
"_id": [
"(ObjectId('5a779b5cf4fa724121269be8'), ObjectId('ffffffffffffffffffffffff')]"
]
}
}
}
}
Note that the winning plan doesn't contain a SORT_KEY_GENERATOR stage. This means that the index can be fully utilized to respond to the sorted query.
I believe there are 2 problems both having to do with your sort. These problems come straight from the documentations but if you would comment I'll help explain (and might possibly learn something myself)
The first and biggest problem is that you must sort in the order given by the index. From docs:
You can specify a sort on all the keys of the index or on a subset;
however, the sort keys must be listed in the same order as they appear
in the index. For example, an index key pattern { a: 1, b: 1 } can
support a sort on { a: 1, b: 1 } but not on { b: 1, a: 1 }.
This means that you must sort in the order given by your winning plan: category, _id, sticky, lastPostAt (or any prefix of that order such as category, _id, sticky or category _id). If not mongodb will identify the 772 docs which are indexed using your winning plan, but will then have to comb through each key in order to assess values and provide the desired sort order. If you want to sort by the order you are curenttly querying must provide a index in that order:
The second problem is that you must sort in the direction that you provided by the index (or the inverse direction).
For a query to use a compound index for a sort, the specified sort
direction for all keys in the cursor.sort() document must match the
index key pattern or match the inverse of the index key pattern. For
example, an index key pattern { a: 1, b: -1 } can support a sort on {
a: 1, b: -1 } and { a: -1, b: 1 } but not on { a: -1, b: -1 } or {a:
1, b: 1}.
Because your indexes are all in ascending order, you would have to either sort in ascending order for all indexes, or descending order for all indexes. If not we run into the same problem in which mongo finds all the relevant docs, but has to comb through them to provide the desired order.
I believe you would get imporved functionality by providing an additional index of:
{ sticky: -1, lastPostAt: -1, _id: 1 }
or its inverse:
{ sticky: 1, lastPostAt: 1, _id: -1 }
This would create a situation where mongo uses your first index
{ category: 1, _id: 1 }
To identify potential unsorted documents, then uses the one of the new index (provided above) since they would already be sorted. Then the limit would take care of giving you your 25 docs.
I'm pretty sure this would created a covered query (a query with no docs examined). Let me know how it goes, cheers!

Mongo date range index with filters

We have the below query
db.Comment.find(
{
$and: [
{ reportCount: { $gt: 0 } },
{ assignee: { $exists: false } },
{ creationDate: { $gt: new Date(1507831097809) } },
{ creationDate: { $lt: new Date(1508522297966) } },
{ siteId: 'MAIN' },
{ parent: { $exists: false } },
{ status: 'ACTIVE' }
]
})
.sort({ creationDate: 1 })
And we have an index
{
"v" : 2,
"key" : {
"creationDate" : 1,
"reportCount" : 1,
"label" : 1
}
}
Here are explain results:
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "myNameSpace",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"creationDate" : {
"$lt" : ISODate("2017-10-20T17:58:17.966Z")
}
},
{
"creationDate" : {
"$gt" : ISODate("2017-10-12T17:58:17.809Z")
}
},
{
"reportCount" : {
"$gt" : 0.0
}
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
}
]
},
"winningPlan" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"creationDate" : 1.0,
"reportCount" : 1.0,
"label" : 1.0
},
"indexName" : "creationDate_1_reportCount_1_label_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"creationDate" : [],
"reportCount" : [],
"label" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"creationDate" : [
"(new Date(1507831097809), new Date(1508522297966))"
],
"reportCount" : [
"(0.0, inf.0]"
],
"label" : [
"[MinKey, MaxKey]"
]
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"creationDate" : 1.0
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
},
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"creationDate" : {
"$lt" : ISODate("2017-10-20T17:58:17.966Z")
}
},
{
"creationDate" : {
"$gt" : ISODate("2017-10-12T17:58:17.809Z")
}
},
{
"reportCount" : {
"$gt" : 0.0
}
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"parent" : 1.0
},
"indexName" : "parent_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"parent" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"parent" : [
"[null, null]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"creationDate" : 1.0
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"creationDate" : {
"$lt" : ISODate("2017-10-20T17:58:17.966Z")
}
},
{
"creationDate" : {
"$gt" : ISODate("2017-10-12T17:58:17.809Z")
}
},
{
"reportCount" : {
"$gt" : 0.0
}
},
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"assignee" : 1.0
},
"indexName" : "assignee_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"assignee" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"assignee" : [
"[null, null]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"creationDate" : 1.0
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"creationDate" : {
"$lt" : ISODate("2017-10-20T17:58:17.966Z")
}
},
{
"creationDate" : {
"$gt" : ISODate("2017-10-12T17:58:17.809Z")
}
},
{
"reportCount" : {
"$gt" : 0.0
}
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
}
]
},
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"siteId" : 1.0,
"updatedDate" : 1.0,
"label" : 1.0
},
"indexName" : "siteId_1_updatedDate_1_label_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"siteId" : [],
"updatedDate" : [],
"label" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"siteId" : [
"[\"MAIN\", \"MAIN\"]"
],
"updatedDate" : [
"[MinKey, MaxKey]"
],
"label" : [
"[MinKey, MaxKey]"
]
}
}
}
}
},
{
"stage" : "SORT",
"sortPattern" : {
"creationDate" : 1.0
},
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"creationDate" : {
"$lt" : ISODate("2017-10-20T17:58:17.966Z")
}
},
{
"creationDate" : {
"$gt" : ISODate("2017-10-12T17:58:17.809Z")
}
},
{
"reportCount" : {
"$gt" : 0.0
}
}
]
},
"inputStage" : {
"stage" : "AND_SORTED",
"inputStages" : [
{
"stage" : "IXSCAN",
"keyPattern" : {
"parent" : 1.0
},
"indexName" : "parent_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"parent" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"parent" : [
"[null, null]"
]
}
},
{
"stage" : "IXSCAN",
"keyPattern" : {
"assignee" : 1.0
},
"indexName" : "assignee_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"assignee" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"assignee" : [
"[null, null]"
]
}
}
]
}
}
}
}
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 19,
"executionTimeMillis" : 8,
"totalKeysExamined" : 533,
"totalDocsExamined" : 56,
"executionStages" : {
"stage" : "FETCH",
"filter" : {
"$and" : [
{
"siteId" : {
"$eq" : "MAIN"
}
},
{
"status" : {
"$eq" : "ACTIVE"
}
},
{
"$nor" : [
{
"assignee" : {
"$exists" : true
}
}
]
},
{
"$nor" : [
{
"parent" : {
"$exists" : true
}
}
]
}
]
},
"nReturned" : 19,
"executionTimeMillisEstimate" : 0,
"works" : 534,
"advanced" : 19,
"needTime" : 513,
"needYield" : 0,
"saveState" : 20,
"restoreState" : 20,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 56,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 56,
"executionTimeMillisEstimate" : 0,
"works" : 533,
"advanced" : 56,
"needTime" : 476,
"needYield" : 0,
"saveState" : 20,
"restoreState" : 20,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"creationDate" : 1.0,
"reportCount" : 1.0,
"label" : 1.0
},
"indexName" : "creationDate_1_reportCount_1_label_1",
"isMultiKey" : false,
"multiKeyPaths" : {
"creationDate" : [],
"reportCount" : [],
"label" : []
},
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 2,
"direction" : "forward",
"indexBounds" : {
"creationDate" : [
"(new Date(1507831097809), new Date(1508522297966))"
],
"reportCount" : [
"(0.0, inf.0]"
],
"label" : [
"[MinKey, MaxKey]"
]
},
"keysExamined" : 533,
"seeks" : 477,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
},
"ok" : 1.0
}
The query is still taking 700-800 ms to return the data. How can I change the index to make the query run faster? Don't consider "keysExamined" : 533, "seeks" : 477, This data. This is just test data.
Looks like its using an index but only the first field in the index? Also multuKey is false?
A few key points from the explain plan output:
The query addresses the following attributes: siteId, status, creationDate, reportCount, assignee, parent
The winning plan has two stages:
IX_SCAN uses creationDate_1_reportCount_1_label_1, this uses indexed lookups on creationDate and reportCount to identify 56 documents which are then forwarded to the FETCH stage
FETCH receives 56 documents from the IX_SCAN stage and then interrogates these documents to apply the siteId, status, assignee and parent filters. This interrogation causes 37 documents to be discarded resulting in 19 document to be returned.
So, your index covers just 2 of the 6 attributes in your query and the remaining 4 attributes in your query are applied by examining the documents not the index. If you want this query to be fully index covered then create the following index:
db.collection.createIndex(
{siteId: 1, status: 1, creationDate: 1, reportCount: 1, assignee: 1, parent: 1}
)
If you re run with this index in place then you should find that (a) MongoDB chooses this index and (b) the number of documents forwarded by the IX_SCAN stage is the same as the number of documents returned by your find call.
I say "should find" because there are other aspects here which might result in MongoDB choosing a different index e.g. use of $nor and the sort stage (creationDate: 1). I would recommend tweaking the index and running with explain 'on' after each tweak and looking for these key items in the executionStats sub document:
"nReturned"
"totalKeysExamined"
"totalDocsExamined"
A simple rule of thumb is this: the closer totalKeysExamined is to nReturned and the closer totalDocsExamined is to zero ... the better your index coverage.
There is also the question of the cost of an index (in terms of impact on write times and index storage) so I'd suggest considering your non functional requirements - can your desired elapsed times be achieved without full index coverage? If not, then you should proceed with empirical testing but be prepared to tweak your choice in reponse to what the explain() output tells you.