2013-12-23 72 views
1

我试图在MongoDB中对一个大集合执行查询,实际上查询由两部分组成,并且总共需要执行大约900ms,我需要它快得多。MongoDB,用bigdata缓慢查询

这些都是收藏品,stoptimes

> 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 

我试图找到里面的每个不同的车次ROUTE_ID这里收集是trip_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中壳查询:

> 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'}}} 
... ]) 

这是一块,我使用,请注意,这是Node.js的+猫鼬的JavaScript,但它应该很容易阅读因为它是平凡的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'}}} 
... ]) 

回答

1

这就像你正在运行匹配任何记录所有行程(204884人次)合计方法阵列。如果这是真的,你正在处理约228记录/毫秒,这是非常好的。

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

不要用我++,除非你有这方面的具体原因,总是把它写成++ i和把你的计数独立变量

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是一个相当复杂的字符串,即24893A459B661,字符串比较总是比整数比较慢。此外,比赛将不得不为每个将测试的比赛提供指定的json行。

一些选项

  • 重新考虑你的stoptimes和旅行的对象,最好的捷径是与一个整数值
  • 创建所有trip_id的是更小,更快地运行匹配的索引列表,以取代trip_id朝向,您应该将关联对象的INDEX存储在行程和/或停止时间;即t_index和s_index
  • 创建配置以保持在静态存储器的车次和stoptimes Web服务,让你的比赛有

我非常个人的看法是,MongoDB的和类似的发动机是不是真的有尚未处理这些与常规关系数据库引擎相比的一种操作,即SQL Server,MySQL,PostgreSQL

1

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

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

我会假设“trip_id + stop_id”在“stoptimes”集合中是唯一的,而“route_id”在“trips”集合中是唯一的。

让我们对数据进行非规格化。 保持“stoptimes”集合进行停止的详细信息,但让我们添加一些信息的进入“旅行”收藏:

{ 
"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”的索引,你的查询应该非常快。

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

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