0%

MongoDB $or操作索引优化

背景

现有索引

1
2
{ distributorNo: 1, username: 1 }
{ newAppleUserId: 1, distributorNo: 1 }

现在有个查询

1
2
3
4
5
6
7
8
db.user.findOne({
distributorNo,
$or: [{
username: appleUserId
}, {
newAppleUserId: appleUserId
}]
})

请问这个查询会走哪个索引?

场景

在网上查询资料后发现$or的查询需要每条查询语句都有索引,其实就是MongoDB把每条查询语句都执行了一遍,再将结果合并.

现在用户表有数据3000w+,发现这个查询每次都超时(设置了最长查询时间6秒),所以判断这条查询语句没有走索引,但是按原来的逻辑,{ distributorNo: 1, username: 1 }{ newAppleUserId: 1, distributorNo: 1 }的索引都有啊.

准备使用.explain(),查看这条查询语句到底是怎么执行的,但是正式数据库数量太多,执行也超时了,就改在本地测试,结果如下

原来MongoDB是先查询了distributorNo,后再进行{distributorNo,username}的查询,在第一步的时候就超时了,因为满足distributorNo条件的数据量占了绝大多数.

解决方案

将查询语句改写

1
2
3
4
5
6
7
8
9
db.user.findOne({
$or: [{
distributorNo,
username: appleUserId
}, {
distributorNo,
newAppleUserId: appleUserId
}]
})

也就是不管执行的查询语句是

1
2
3
4
5
6
db.user.findOne({
{
distributorNo,
username: appleUserId
}
})

还是

1
2
3
4
5
6
db.user.findOne({
{
distributorNo,
newAppleUserId: appleUserId
}
})

都有对应的索引.
部署以后观察,慢查询与超时都消失了

总结

MongoDB 的$or查询其实就是将每种情况都执行了一次
如果$or条件之前还有其他条件,MongoDB会先用该条件查询,后再执行$or里的每一条查询
所以要么将前置条件合并到$or语句里,要么对前置条件单独建索引