【问题标题】:MongoDB, slow query with bigdataMongoDB,大数据的慢查询
【发布时间】:2013-12-23 15:48:11
【问题描述】:

我正在尝试对 mongodb 中的一个大集合执行查询,实际上该查询由两部分组成,总共需要大约 900 毫秒才能执行,我需要它更快。

这些是集合,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

我正在尝试在 trips 集合中查找每个不同的 route_id,其中trip_id 等于与停止时间中给定的 stop_id 匹配的每个行程 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'}}}
... ])

【问题讨论】:

    标签: javascript node.js mongodb mongoose gtfs


    【解决方案1】:

    看起来您正在对匹配数组中任何记录的所有行程(204884 次行程)运行聚合方法。如果这是真的,那么您正在处理大约 228 条记录/毫秒,这非常好。

    您可以在代码中进行一些明显的优化

    除非有特定原因,否则切勿使用 i++,始终将其写为 ++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 行。

    一些选项

    • 重新考虑停止时间和行程的对象,最好的捷径是将trip_id 替换为整数值
    • 创建一个包含所有trip_id 的索引列表,该列表可以更小更快地运行匹配,您应该将关联对象的INDEX 存储在trips 和/或stoptimes 中; IE。 t_index 和 s_index
    • 创建一个配置为将行程和停止时间保存在静态内存中并在那里进行匹配的 Web 服务

    我非常个人的看法是,与常规的关系数据库引擎相比,MongoDB 和类似的引擎还没有真正处理这类操作,即SQL Server、MySQL、PostgreSQL.

    【讨论】:

      【解决方案2】:

      确保您在“trips”集合中的“trip_id”上有一个索引。 即使使用索引,如果您为“arr”提供一长串值,您也不会获得最佳性能。 '$in' 运算符很难优化,因为必须查看每个值。例如,如果 'arr' 数组有 10 个值,则必须搜索每个值的索引。它基本上看起来像 10 个子查询。

      您可以设计架构以避免使用“$in”运算符、查找 2 个集合并使用聚合框架。

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

      让我们对数据进行非规范化。 保留“stoptimes”集合以携带有关站点的详细信息,但让我们将其中一些信息添加到“trips”集合中:

      {
      "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 的建议可能会满足您的需要。如果您决定保留聚合框架解决方案,您可以评估聚合管道第一步的性能。运行以下命令以确保 $match 步骤使用索引。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-18
        • 1970-01-01
        • 2020-05-20
        • 2021-04-01
        相关资源
        最近更新 更多