关于3种sql,两个索引,对应的mongodb优化选择的是哪个索引呢?
脚本1
db.QuickReplyTemplate.find({
"sceneKey": "responseMsg",
"termId": {"$in":["1","2","3"]},
"isDelete": false
}).sort({"updatedOn":-1}).explain("executionStats");
脚本2
db.QuickReplyTemplate.find({
"sceneKey": "responseMsg",
"termId": "1",
"isDelete": false
}).sort({"updatedOn":-1}).explain("executionStats");
脚本3
db.QuickReplyTemplate.find({
"sceneKey": "responseMsg",
"isDelete": false
}).sort({"updatedOn":-1}).explain("executionStats");
索引1
db.getCollection("QuickReplyTemplate").createIndex({
sceneKey: NumberInt("1"),
termId: NumberInt("1"),
isDelete: NumberInt("1"),
updatedOn: NumberInt("1")
}, {
name: "idx_QuickReplyTemplate__sceneKey_1_termId_1_isDelete_1_updatedOn_1"
});
db.getCollection("QuickReplyTemplate").createIndex({
sceneKey: NumberInt("1"),
isDelete: NumberInt("1"),
updatedOn: NumberInt("1")
}, {
name: "idx_QuickReplyTemplate__sceneKey_1_isDelete_1_updatedOn_1"
});
分析:
我们先大胆猜测一下,脚本1使用的是哪个索引?脚本2使用的是哪个索引?脚本3呢?
此处减少空间,直接粘出最优选择
这是脚本1的最优选择(是的,我们可以看出,他选择了索引2,并没有选择索引1)
至于为什么没有选择索引1呢?因为他排除了,我们去看看
这里没有粘出所有分析计划,但是我们可以从上述看出,他使用了排序合并的计划,也就是 $in关键字导致,所以他认为,范围查询,不能精确命中,所以将其排除;
那么看看脚本2吧,脚本2就是为了对比脚本1,看看他选择了什么索引
是的,可以和我们预料的是一致,由于此次termId字段只有一个值,那么他将选择索引1,因为命中率更高。
那么我们看看脚本3的执行计划吧
毋庸置疑,索引2更为合适,那么此处就不用分析了,很清晰
他如果选择了索引1,那么就会出现索引中断现象,那么对于后面的排序,那么就会出现内存排序现象
虽然是在索引命中的前提下进行的内存排序
结论:那么好坏对比,相比大家都分析出来了。最好的解决方法就是两个索引都留着,都可以选择最优的索引。
db.getCollection("QuickReplyTemplate").createIndex({
sceneKey: NumberInt("1"),
termId: NumberInt("1"),
isDelete: NumberInt("1"),
updatedOn: NumberInt("1")
}, {
name: "idx_QuickReplyTemplate__sceneKey_1_termId_1_isDelete_1_updatedOn_1"
});
db.getCollection("QuickReplyTemplate").createIndex({
sceneKey: NumberInt("1"),
isDelete: NumberInt("1"),
updatedOn: NumberInt("1")
}, {
name: "idx_QuickReplyTemplate__sceneKey_1_isDelete_1_updatedOn_1"
});
但是大家没有发现,两个索引基本是很类似的,那么如果两个都留下,很显然,并不是一个很好的方式,这种复合索引设计,不是很好的一个设计方案,必须选择留下其一。
那到底留下哪个?其实得根据具体业务来,上述3个脚本,都是笔者业务中会出现的查询条件组合,那么笔者需要根据业务出现的频率,并且sql面向的对象进行选择取舍。
最后笔者,还是选择了留下索引2。
这个文章给大家提供一个思路,一个分析项目执行效率的优化角度方案,思路。