背景
最近在做社区tab开发,新加了一个帖子排序:按回复时间排序。新回复时间是新加的字段。半个月左右后,测试说接口有时候会炸弹(失败),响应比较慢。
数据库使用的mongodb。
问题
我看了查询,写了一个测试查询,expain(),发现使用的索引是其他人创建的索引,我没有专门为查询条件建索引。
解决方案
将查询发给了DBA,向他请教了下,并对两种方案的执行计划进行了对比,记录一下。
查询
db.posts.find({tid:23, c_type:{$in:[1]}, status:{$in:[1,2,3]}, ct:{$gte:1612835946}}).sort({topic_reply_lut:-1}).skip(0).limit(10).explain()
对话
DBA: 你这个最大查询结果是多少?不加limit
我:2-3万
DBA:tid:23, c_type:{$in:[1]}, status:{$in:[1,2,3]} 如果这3个条件筛选出来数据了不大 就可以 (tid,c_type,status,topic_reply_lut,ct) 组合。如果数据量大,(tid,c_type,status,ct,topic_reply_lut),这样加排序就走不了索引。
DBA:如果加上ct条件,最总出来的结果比较少,排序如果不走索引应该问题也不大。
我:(tid,c_type,status,topic_reply_lut,ct) 这个查询的时候索引可以用到ct吗?
DBA:应该可以
我:加上ct 有2-3w数据
DBA:那就这样加吧。你最好在测试环境试试这两种索引的性能,分别看看执行计划。
DBA:主要就看两个索引,文档和索引分别扫描了多少行, 取limit10 如果都是扫描10行,那就是比较好的。
我:好。(结果在最下面)
DBA:你可以强制让走另一个索引,看看效果。
我:(结果在最下面)
DBA:从这个上就能看出来,这个索引没有完全覆盖,ct范围把后续排序阻断了,导致limit 10也就不起作用了。
DBA:像第二个就是如果最终结果有50000结果,就会每次扫描50000,然后在取10条,性能就很差。
DBA:用limit 必须索引全部覆盖,才能实现 取10条,扫描10条。
我:(tid,c_type,status,topic_reply_lut,ct),topic_reply_lut在sort里,为什么ct也可以用到索引。
DBA:业务索引ct范围查询是在最后,所以不会影响。覆盖索引中查询条件只要有 范围查询,不等于,非等值,就会截断后续索引。
我:topic_reply_lut没在where里, topic_reply_lut和ct也可以用到所以,这个意思
DBA:但是在一个索引文件里面啊
DBA:你把ct改成=值查询,应该就会走第二个查询,而且应该 都会只扫描 10行
(我验证了确实)
DBA:正常情况筛选先 where 然后在排序。
我:我查询里是范围查询,截断了,没有用到索引里的topic_reply_lut
DBA:对的,这样就会导致 limit 10也会失效,虽然只返回了10条, 但是会把索引结果查询出来,再取10条,所以一般组合索引顺序,只要是范围或者是非等值的都排最后
DBA:所以你以后看执行计划只用关注这个位置就行,其它的没啥意义。
我:太香了,学到了。
(tid,c_type,status,topic_reply_lut,ct) 执行计划如下
post:PRIMARY> db.posts.find({tid:3, c_type:{$in:[1]}, status:{$in:[1,2,3]}, ct:{$gte:1600000000}}).sort({topic_reply_lut:-1}).skip(0).limit(10).explain(executionStats=true)
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "post.posts",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"tid" : {
"$eq" : 3
}
},
{
"ct" : {
"$gte" : 1600000000
}
},
{
"c_type" : {
"$in" : [
1
]
}
},
{
"status" : {
"$in" : [
1,
2,
3
]
}
}
]
},
"winningPlan" : {
"stage" : "LIMIT",
"limitAmount" : 10,
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "SORT_MERGE",
"sortPattern" : {
"topic_reply_lut" : -1
},
"inputStages" : [
{
"stage" : "IXSCAN",
"keyPattern" : {
"tid" : 1,
"c_type" : 1,
"status" : 1,
"topic_reply_lut" : -1,
"ct" : -1
},
"indexName" : "tid_1_c_type_1_status_1_topic_reply_lut_-1_ct_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"tid" : [
"[3.0, 3.0]"
],
"c_type" : [
"[1.0, 1.0]"
],
"status" : [
"[1.0, 1.0]"
],
"topic_reply_lut" : [
"[MaxKey, MinKey]"
],
"ct" : [
"[inf.0, 1600000000.0]"
]
}
},
{
"stage" : "IXSCAN",
"keyPattern" : {
"tid" : 1,
"c_type" : 1,
"status" : 1,
"topic_reply_lut" : -1,
"ct" : -1
},
"indexName" : "tid_1_c_type_1_status_1_topic_reply_lut_-1_ct_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"tid" : [
"[3.0, 3.0]"
],
"c_type" : [
"[1.0, 1.0]"
],
"status" : [
"[2.0, 2.0]"
],
"topic_reply_lut" : [
"[MaxKey, MinKey]"
],
"ct" : [
"[inf.0, 1600000000.0]"
]
}
},
{
"stage" : "IXSCAN",
"keyPattern" : {
"tid" : 1,
"c_type" : 1,
"status" : 1,
"topic_reply_lut" : -1,
"ct" : -1
},
"indexName" : "tid_1_c_type_1_status_1_topic_reply_lut_-1_ct_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"tid" : [
"[3.0, 3.0]"
],
"c_type" : [
"[1.0, 1.0]"
],
"status" : [
"[3.0, 3.0]"
],
"topic_reply_lut" : [
"[MaxKey, MinKey]"
],
"ct" : [
"[inf.0, 1600000000.0]"
]
}
}
]
}
}
},
"rejectedPlans" : [
{
"stage" : "SORT",
"sortPattern" : {
"topic_reply_lut" : -1
},
"limitAmount" : 10,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"tid" : 1,
"c_type" : 1,
"status" : 1,
"ct" : -1,
"topic_reply_lut" : -1
},
"indexName" : "tid_1_c_type_1_status_1_ct_-1_topic_reply_lut_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"tid" : [
"[3.0, 3.0]"
],
"c_type" : [
"[1.0, 1.0]"
],
"status" : [
"[1.0, 1.0]",
"[2.0, 2.0]",
"[3.0, 3.0]"
],
"ct" : [
"[inf.0, 1600000000.0]"
],
"topic_reply_lut" : [
"[MaxKey, MinKey]"
]
}
}
}
}
}
]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 10,
"executionTimeMillis" : 0,
"totalKeysExamined" : 12,
"totalDocsExamined" : 10,
"executionStages" : {
"stage" : "LIMIT",
"nReturned" : 10,
"executionTimeMillisEstimate" : 0,
"works" : 23,
"advanced" : 10,
"needTime" : 12,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"limitAmount" : 10,
"inputStage" : {
"stage" : "FETCH",
"nReturned" : 10,
"executionTimeMillisEstimate" : 0,
"works" : 22,
"advanced" : 10,
"needTime" : 12,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 0,xQ
"invalidates" : 0,
"docsExamined" : 10,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "SORT_MERGE",
"nReturned" : 10,
"executionTimeMillisEstimate" : 0,
"works" : 22,
"advanced" : 10,
"needTime" : 12,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 0,
"invalidates" : 0,
"sortPattern" : {
"topic_reply_lut" : -1
},
"dupsTested" : 11,
"dupsDropped" : 0,
"inputStages" : [
{
"stage" : "IXSCAN",
"nReturned" : 9,
"executionTimeMillisEstimate" : 0,
"works" : 9,
"advanced" : 9,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 0,
"invalidates" : 0,
"keyPattern" : {
"tid" : 1,
"c_type" : 1,
"status" : 1,
"topic_reply_lut" : -1,
"ct" : -1
},
"indexName" : "tid_1_c_type_1_status_1_topic_reply_lut_-1_ct_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"tid" : [
"[3.0, 3.0]"
],
"c_type" : [
"[1.0, 1.0]"
],
"status" : [
"[1.0, 1.0]"
],
"topic_reply_lut" : [
"[MaxKey, MinKey]"
],
"ct" : [
"[inf.0, 1600000000.0]"
]
},
"keysExamined" : 9,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
},
{
"stage" : "IXSCAN",
"nReturned" : 2,
"executionTimeMillisEstimate" : 0,
"works" : 2,
"advanced" : 2,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 0,
"invalidates" : 0,
"keyPattern" : {
"tid" : 1,
"c_type" : 1,
"status" : 1,
"topic_reply_lut" : -1,
"ct" : -1
},
"indexName" : "tid_1_c_type_1_status_1_topic_reply_lut_-1_ct_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"tid" : [
"[3.0, 3.0]"
],
"c_type" : [
"[1.0, 1.0]"
],
"status" : [
"[2.0, 2.0]"
],
"topic_reply_lut" : [
"[MaxKey, MinKey]"
],
"ct" : [
"[inf.0, 1600000000.0]"
]
},
"keysExamined" : 2,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
},
{
"stage" : "IXSCAN",
"nReturned" : 0,
"executionTimeMillisEstimate" : 0,
"works" : 1,
"advanced" : 0,
"needTime" : 0,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"tid" : 1,
"c_type" : 1,
"status" : 1,
"topic_reply_lut" : -1,
"ct" : -1
},
"indexName" : "tid_1_c_type_1_status_1_topic_reply_lut_-1_ct_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"tid" : [
"[3.0, 3.0]"
],
"c_type" : [
"[1.0, 1.0]"
],
"status" : [
"[3.0, 3.0]"
],
"topic_reply_lut" : [
"[MaxKey, MinKey]"
],
"ct" : [
"[inf.0, 1600000000.0]"
]
},
"keysExamined" : 1,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
]
}
}
},
},
"serverInfo" : {
"host" : "sgal-omg-test-db01",
"port" : 27048,
"version" : "3.2.7",
"gitVersion" : "4249c1d2b5999ebbf1fdf3bc0e0e3b3ff5c0aaf2"
},
"ok" : 1
}
(tid,c_type,status,ct,topic_reply_lut)执行计划如下:
ost:PRIMARY> db.posts.find({tid:3, c_type:{$in:[1]}, status:{$in:[1,2,3]}, ct:{$gte:1600000000}}).sort({topic_reply_lut:-1}).skip(0).limit(10).hint({"tid":1,"c_type":1,"status":1,"ct":-1,"topic_reply_lut":-1}).explain(executionStats=true)
{
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "post.posts",
"indexFilterSet" : false,
"parsedQuery" : {
"$and" : [
{
"tid" : {
"$eq" : 3
}
},
{
"ct" : {
"$gte" : 1600000000
}
},
{
"c_type" : {
"$in" : [
1
]
}
},
{
"status" : {
"$in" : [
1,
2,
3
]
}
}
]
},
"winningPlan" : {
"stage" : "SORT",
"sortPattern" : {
"topic_reply_lut" : -1
},
"limitAmount" : 10,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"inputStage" : {
"stage" : "FETCH",
"inputStage" : {
"stage" : "IXSCAN",
"keyPattern" : {
"tid" : 1,
"c_type" : 1,
"status" : 1,
"ct" : -1,
"topic_reply_lut" : -1
},
"indexName" : "tid_1_c_type_1_status_1_ct_-1_topic_reply_lut_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"tid" : [
"[3.0, 3.0]"
],
"c_type" : [
"[1.0, 1.0]"
],
"status" : [
"[1.0, 1.0]",
"[2.0, 2.0]",
"[3.0, 3.0]"
],
"ct" : [
"[inf.0, 1600000000.0]"
],
"topic_reply_lut" : [
"[MaxKey, MinKey]"
]
}
}
}
}
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 10,
"executionTimeMillis" : 0,
"totalKeysExamined" : 50,
"totalDocsExamined" : 47,
"executionStages" : {
"stage" : "SORT",
"nReturned" : 10,
"executionTimeMillisEstimate" : 0,
"works" : 62,
"advanced" : 10,
"needTime" : 51,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"sortPattern" : {
"topic_reply_lut" : -1
},
"memUsage" : 5574,
"memLimit" : 33554432,
"limitAmount" : 10,
"inputStage" : {
"stage" : "SORT_KEY_GENERATOR",
"nReturned" : 0,
"executionTimeMillisEstimate" : 0,
"works" : 51,
"advanced" : 0,
"needTime" : 3,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"inputStage" : {
"stage" : "FETCH",
"nReturned" : 47,
"executionTimeMillisEstimate" : 0,
"works" : 50,
"advanced" : 47,
"needTime" : 2,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"docsExamined" : 47,
"alreadyHasObj" : 0,
"inputStage" : {
"stage" : "IXSCAN",
"nReturned" : 47,
"executionTimeMillisEstimate" : 0,
"works" : 50,
"advanced" : 47,
"needTime" : 2,
"needYield" : 0,
"saveState" : 0,
"restoreState" : 0,
"isEOF" : 1,
"invalidates" : 0,
"keyPattern" : {
"tid" : 1,
"c_type" : 1,
"status" : 1,
"ct" : -1,
"topic_reply_lut" : -1
},
"indexName" : "tid_1_c_type_1_status_1_ct_-1_topic_reply_lut_-1",
"isMultiKey" : false,
"isUnique" : false,
"isSparse" : false,
"isPartial" : false,
"indexVersion" : 1,
"direction" : "forward",
"indexBounds" : {
"tid" : [
"[3.0, 3.0]"
],
"c_type" : [
"[1.0, 1.0]"
],
"status" : [
"[1.0, 1.0]",
"[2.0, 2.0]",
"[3.0, 3.0]"
],
"ct" : [
"[inf.0, 1600000000.0]"
],
"topic_reply_lut" : [
"[MaxKey, MinKey]"
]
},
"keysExamined" : 50,
"dupsTested" : 0,
"dupsDropped" : 0,
"seenInvalidated" : 0
}
}
}
}
},
"serverInfo" : {
"host" : "sgal-omg-test-db01",
"port" : 27048,
"version" : "3.2.7",
"gitVersion" : "4249c1d2b5999ebbf1fdf3bc0e0e3b3ff5c0aaf2"
},
"ok" : 1
}