1

mongodb の大きなコレクションに対してクエリを実行しようとしていますが、実際にはクエリは 2 つの部分で構成されており、実行に合計約 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

trip_id が stoptimes の特定の stop_id に一致する各 trip id と等しい trips コレクション内のすべての個別の route_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'}}}
... ])

これは私が使用している 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'}}}
... ])
4

2 に答える 2

1

これは、配列内の任意のレコードに一致するすべての旅行 (204884 回の旅行) で集計メソッドを実行しているようです。それが本当なら、1 ミリ秒あたり約 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に格納する必要があります。つまり、t_index と s_index
  • 移動と停車時刻を静的メモリに保持するように構成された Web サービスを作成し、そこで一致を作成します

私の非常に個人的な意見では、MongoDB や類似のエンジンは、 SQL Server、MySQL、PostgreSQL などの通常のリレーショナル データベース エンジンと比較して、この種の操作を処理するにはまだ実際には存在していません。

于 2013-12-23T16:54:20.980 に答える
1

「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})
于 2014-01-09T16:02:40.303 に答える