Mongodb$lookup聚合查询需要11+;秒,我如何优化这一点
我有一个包含3个集合的股价警报应用程序 1) 警报 2) 以美元表示的资产价值每2分钟更新一次,目前拥有2000资产Mongodb$lookup聚合查询需要11+;秒,我如何优化这一点,mongodb,mongoose,mongodb-query,aggregation-framework,Mongodb,Mongoose,Mongodb Query,Aggregation Framework,我有一个包含3个集合的股价警报应用程序 1) 警报 2) 以美元表示的资产价值每2分钟更新一次,目前拥有2000资产 { "_id" : "xyz", //asset id "1" : 997, //current price of the asset } { "_id" : "EUR", "1" : 0.811798 } 3) 美元的法定兑换率每小时更新一次,目前有160种货币 { "_id" : "xyz", //asset id "1"
{
"_id" : "xyz", //asset id
"1" : 997, //current price of the asset
}
{
"_id" : "EUR",
"1" : 0.811798
}
3) 美元的法定兑换率每小时更新一次,目前有160种货币
{
"_id" : "xyz", //asset id
"1" : 997, //current price of the asset
}
{
"_id" : "EUR",
"1" : 0.811798
}
人员必须能够设置一个警报,说明“如果欧元xyz低于1000或高于1000,请提醒我” 对于测试数据,我设置了100000个警报,第一步是从我加入的资产表中查找资产xyz的当前价格
Alert
.aggregate([
{
$lookup: {
from: "assets",
localField: "2", //field containing asset id
foreignField: "_id", //foreign field with asset id
as: "s"
}
}
])
.allowDiskUse(true)
.exec((error, result) => {
if (error) {
console.log(error)
}
else {
console.log("Got", result.length, "documents mongoose")
}
mongoose.connection.close()
})
目前仅此一项就需要11秒强>
我希望能够将每个资产的当前价格乘以指定的货币,然后检查它是否高于或低于用户设置的水平,以便触发警报
例如,如果警报为xyz:EUR,我想得到xyz的资产价格(以美元为单位),美元对欧元的价格,并将两者相乘以得到最终价格xyz:EUR,然后检查该值是否大于或小于1000以触发警报
外部字段是一个_id字段,我假设默认情况下它是索引字段。我在localField 2:上设置了一个索引,它是我的资产id,3:是我的菲亚特符号
下面是对我的目标进行getIndexes()查询的结果
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.alerts"
},
{
"v" : 2,
"key" : {
"2" : 1
},
"name" : "2_1",
"ns" : "test.alerts",
"background" : true
},
{
"v" : 2,
"key" : {
"3" : 1
},
"name" : "3_1",
"ns" : "test.alerts",
"background" : true
}
]
我还运行了一个explain()来检查正在发生的事情,尽管设置了索引,但它仍然指示一个COLLSCAN
{
"stages": [
{
"$cursor": {
"query": {},
"queryPlanner": {
"plannerVersion": 1,
"namespace": "test.alerts",
"indexFilterSet": false,
"parsedQuery": {},
"winningPlan": {
"stage": "COLLSCAN",
"direction": "forward"
},
"rejectedPlans": []
}
}
},
{
"$lookup": {
"from": "assets",
"as": "s",
"localField": "2",
"foreignField": "_id"
}
}
],
"ok": 1
}
任何建议、建议都会非常有用。多谢各位
备选方案
我真的不想做的另一个选择是计算代码中的所有价格,并向警报集合添加另一个名为price的字段,该字段每2分钟更新一次xyz:EUR和其他警报。潜在的1500项资产x 160辆菲亚特将意味着每2分钟就有大量的进入
更新1探查器输出
钥匙没有被使用!有什么想法吗
getmore test.e1_sources 89ms Tue Feb 27 2018 19:22:28
command:{
"getMore" : NumberLong("6524439989055389783"),
"collection" : "alerts",
"batchSize" : 1000,
"$readPreference" : {
"mode" : "secondaryPreferred"
},
"$db" : "test"
} originatingCommand:{
"aggregate" : "alerts",
"pipeline" : [
{
"$lookup" : {
"from" : "assets",
"localField" : "2",
"foreignField" : "_id",
"as" : "s"
}
}
],
"allowDiskUse" : true,
"cursor" : {
"batchSize" : 1000
},
"$db" : "test"
} cursorid:NumberLong("6524439989055389783") keysExamined:0 docsExamined:0 cursorExhausted numYield:2 locks:{
"Global" : {
"acquireCount" : {
"r" : NumberLong(6000)
}
},
"Database" : {
"acquireCount" : {
"r" : NumberLong(3000)
}
},
"Collection" : {
"acquireCount" : {
"r" : NumberLong(2999)
}
}
} nreturned:1000 responseLength:164393 protocol:op_query planSummary:COLLSCAN client:127.0.0.1 allUsers:[ ] user:
人员必须能够设置一个警报,说明“如果欧元xyz低于1000或高于1000,请提醒我” 根据你提到的这一行。添加匹配阶段以筛选该用户id的assetid。这将减少您必须加入的数据量
Alert.aggregate([
{
$match:{
"1"://user id,
"2"://asset id
}
},
{
$lookup: {
from: "assets",
localField: "2", //field containing asset id
foreignField: "_id", //foreign field with asset id
as: "s"
}
}
])
.allowDiskUse(true)
.exec((error, result) => {
if (error) {
console.log(error)
}
else {
console.log("Got", result.length, "documents mongoose")
}
mongoose.connection.close()
})
感谢您的回答,但一个主要问题是,我需要每2分钟触发设置不同用户的所有警报,警报不仅仅针对单个用户,我注意到localField不使用任何索引,无论您是否设置了索引。使用查找时,这意味着左键连接,它不在本地字段上使用索引,因为您是基于一列将左表中的所有值连接到右表中。这就是为什么它会做一个乌尔斯坎。