Performance 使用$all和$elemMatch时,MongoDB会扫描整个索引
我有一个用户文档集合,其中每个用户都可以拥有任意一组属性。每个用户都关联到一个应用程序文档。以下是一个示例用户:Performance 使用$all和$elemMatch时,MongoDB会扫描整个索引,performance,mongodb,indexing,Performance,Mongodb,Indexing,我有一个用户文档集合,其中每个用户都可以拥有任意一组属性。每个用户都关联到一个应用程序文档。以下是一个示例用户: { "appId": "XXXXXXX", "properties": [ { "name": "age", "value": 30 }, { "name": "gender", "value": "female" }, { "name": "alive", "value": true } ] } 我希望能够根
{
"appId": "XXXXXXX",
"properties": [
{ "name": "age", "value": 30 },
{ "name": "gender", "value": "female" },
{ "name": "alive", "value": true }
]
}
我希望能够根据用户属性的值查找/统计用户。例如,查找属性Y>10且Z等于true的应用程序X的所有用户
我在这个集合上有一个复合的多键索引db.users.ensureIndex({“appId”:1,“properties.name”:1,“properties.value”:1})
。此索引适用于单条件查询,例如:
db.users.find({
appId: 'XXXXXX',
properties: {
$elemMatch: {
name: 'age',
value: {
$gt: 10
}
}
}
})
上述查询在小于300毫秒的时间内完成,收集了100万用户。但是,当我尝试添加第二个条件时,性能会显著降低(7-8秒),并且explain()
输出指示正在扫描整个索引以完成查询(“nscaned”:2752228
)
质疑
说明
{
"cursor" : "BtreeCursor appId_1_properties.name_1_properties.value_1",
"isMultiKey" : true,
"n" : 256,
"nscannedObjects" : 1000000,
"nscanned" : 2752228,
"nscannedObjectsAllPlans" : 1018802,
"nscannedAllPlans" : 2771030,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 21648,
"nChunkSkips" : 0,
"millis" : 7425,
"indexBounds" : {
"appId" : [
[
"XXXXX",
"XXXXX"
]
],
"properties.name" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
],
"properties.value" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
},
"filterSet" : false
}
我假设这是因为Mongo无法创建合适的边界,因为我同时查找布尔值和整数值
我的问题是:是否有更好的方法来构造数据,或修改查询以提高性能并更好地利用索引?是否可以指示mongo分别处理每个条件,生成适当的边界,然后执行结果的交集,而不是扫描所有文档?还是mongo不适合这种类型的用例?我知道这是一个老问题,但我认为最好不要使用“名称”和“值”标记来构造数据:
您使用的是MongoDB的2.6版本,对吗?我无法重现这种情况-在3.0-rc8中,查询速度很快,即它不会扫描对象。@mnemosyn是的,我使用的是v2.6.7。也许3.0-rc8可以解决您的问题?至少如果你愿意在生产中使用一个候选发布版本……不幸的是,我认为在3.0正式发布之前,这不是一个选项,因为我们的数据库托管在云端。是的,这种行为预计<3.0。在3.0版本发布后升级将对您有所帮助,因为查询计划已经改进,能够处理这种情况。您真的需要这样的元数据数组吗?如果您能提供帮助的话,这将有助于您现在拥有一个更标准的文档结构。对于我们的用例,这是不可能的,因为我们无法提前知道密钥-它们是用户生成的,并且它们都需要索引/可查询。
{
"cursor" : "BtreeCursor appId_1_properties.name_1_properties.value_1",
"isMultiKey" : true,
"n" : 256,
"nscannedObjects" : 1000000,
"nscanned" : 2752228,
"nscannedObjectsAllPlans" : 1018802,
"nscannedAllPlans" : 2771030,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 21648,
"nChunkSkips" : 0,
"millis" : 7425,
"indexBounds" : {
"appId" : [
[
"XXXXX",
"XXXXX"
]
],
"properties.name" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
],
"properties.value" : [
[
{
"$minElement" : 1
},
{
"$maxElement" : 1
}
]
]
},
"filterSet" : false
}
{
"appId": "XXXXXXX",
"properties": [
{ "age": 30 },
{ "gender: "female" },
{ "alive": true }
]
}