MongoDB,用bigdata慢查询

我试图在mongodb的大集合上执行一个查询,实际上查询是由两部分组成,总共需要执行大约900ms,我需要它快得多。

这些是收集, 停止时间

> db.stoptimes.find().limit(1); { "trip_id": "24893A459B661", "arrival_time": "22:30:00", "departure_time": "22:30:00", "stop_id": "1904", "stop_sequence": 2, "stop_headsign": "", "pickup_type": "0", "drop_off_type": "0", "shape_dist_traveled": "0.88659123054", "agency_key": "alamedaoakland-ferry", "_id": ObjectId("52b394c680052ea30918fd62") } > db.stoptimes.count(); 5959551 

旅行

 > db.trips.find().limit(1); { "route_id": "60", "service_id": "180A536", "trip_id": "23736A180B536", "trip_short_name": "", "trip_headsign": "San Francisco via Pier 41", "direction_id": "", "block_id": "282", "shape_id": "30", "trip_bikes_allowed": "2", "agency_key": "alamedaoakland-ferry", "_id": ObjectId("52b394c780052ea30918ff34") } > db.trips.count(); 204884 

我试图find每个不同的route_id旅行收集里面trip_id等于每个旅行id匹配停止时间给定的stop_id。

 ------ stoptimes --- -> ---------- trips ----------------- stop_id1 -> trip_id1 -> trip_id1 -> route_id1 -> route_id1 -> trip_id2 -> trip_id2 -> route_id2 -> route_id2 -> trip_id3 -> trip_id3 -> route_id2 -> trip_id4 -> trip_id4 -> route_id2 -> trip_id5 -> trip_id5 -> route_id3 -> route_id3 

这是在mongodb shell中的查询:

 > var tripids = db.stoptimes.aggregate([ ... {$match : { 'stop_id' : '1904' }}, ... {$project : { '_id' : 0, 'trip_id' : 1 }} ... ]); > var arr = []; > for(var i=0; i<tripids.result.length; i++) ... { arr.push(tripids.result[i].trip_id); } > db.trips.aggregate([ ... {$match : { 'trip_id' : {$in : arr}}}, ... {$group : { ... _id : "$route_id", ... direction_id : { $first: '$direction_id'}, ... shape_id : {$first : '$shape_id'}}} ... ]) 

这是我使用的JavaScript的一块,请注意它是node.js + mongoose,但它应该很容易阅读,因为它是纯javascript:

 StopTime .aggregate([ {$match : { 'stop_id' : stop_id }}, {$project : { '_id' : 0, 'trip_id' : 1 }} ], function (err, trip_ids){ var arr = []; for(var i=0;i<trip_ids.length;i++) { arr.push(trip_ids[i].trip_id); } Trip .aggregate([ {$match : { 'trip_id' : {$in : arr} }}, {$group : { _id : "$route_id", direction_id : { $first: '$direction_id'}, shape_id : { $first: '$shape_id'} }} ], function (err, route_ids){ cb(err, route_ids); }); }); 

我能做些什么来改善表演?

编辑:

这是唯一需要这么长时间的查询:

 > db.trips.aggregate([ ... {$match : { 'trip_id' : {$in : arr}}}, ... {$group : { ... _id : "$route_id", ... direction_id : { $first: '$direction_id'}, ... shape_id : {$first : '$shape_id'}}} ... ]) 

这看起来像是在所有行程(204884行程)上运行匹配数组中任何logging的聚合方法。 如果这是真的,你正在处理约228logging/毫秒,这是相当不错的。

你可以在你的代码中做一些明显的优化

除非你有特定的原因,否则不要使用i ++,总是把它写成++ i,并把你的计数放在单独的variables中

 var trip_ids_length = trip_ids.length; for(var i=0;i<trip_ids_length;++i) { arr.push(trip_ids[i].trip_id); } 

你的trip_id是一个相当复杂的string, 即24893A459B661 ,string比较总是慢于整数比较。 此外,比赛将不得不为每个将testing的比赛拉动指定的json行。

一些选项

  • 重新考虑你的对象的停工时间和旅行,最好的捷径是用一个整数值代替trip_id
  • 创build一个索引列表,其中所有的trip_id更小更快地运行匹配,您应该将关联对象的INDEX存储在跳闸和/或停止时间 ; 即t_index和s_index
  • 创build一个Web服务configuration为在静态内存中保存旅程和停止时间,并在那里进行匹配

我个人的观点是,MongoDB和类似的引擎还没有处理这种types的操作相比,一个常规的关系数据库引擎,如SQL Server,MySQL,PostgreSQL

确保在“旅行”集合中有“trip_id”的索引。 即使使用索引,如果您提供了“arr”的一长串值,您将无法获得最佳性能。 '$ in'操作符很难优化,因为必须查看每个值。 例如,如果'arr'数组有10个值,那么每个值都必须search索引。 它基本上看起来像10个子查询。

您可以devise您的模式,以避免使用'$ in'操作符,查找2个集合并使用聚合框架。

我将假设“trip_id + stop_id”在“停车时间”集合中是唯一的,而“route_id”在“旅行”集合中是唯一的。

让我们对数据进行非规范化。 保留“停止时间”集合以携带停靠点的详细信息,但是我们将这些信息添加到“旅行”集合中:

 { "route_id": "60", "service_id": "180A536", "trip_id": "23736A180B536", "stop_id" : [ 1800, 1830, 1904] <============== "trip_short_name": "", "trip_headsign": "San Francisco via Pier 41", "direction_id": "", "block_id": "282", "shape_id": "30", "trip_bikes_allowed": "2", "agency_key": "alamedaoakland-ferry", "_id": ObjectId("52b394c780052ea30918ff34") } 

然后你的查询变成:

 db.trips.find({"stop_id":1904}, {"_id":0, "route_id":1, "direction_id":1, "shape_id":1}) 

用“stop_id”索引,你的查询应该是非常快的。

总之,devise您的模式,以便针对最重要的查询进行优化。 如果上述查询是最重要的,那么您将通过新的模式devise获利。 如果这是一个孤立的查询,并且已经针对您的常见情况进行了优化,那么Eric的build议可能只是做您所需要的。 如果您决定保留聚合框架解决scheme,则可以评估聚合pipe道第一步的性能。 运行以下命令以确保$匹配步骤正在使用索引。

 db.collection.runCommand("aggregate", {pipeline: YOUR_PIPELINE, explain: true})