Work Records

日々の作業記録です。ソフトウェアエンジニアリング全般から、趣味の話まで。

Parseのクエリのレスポンスタイムを確認してみた

Parseのクエリのパフォーマンスをちょっと調べてみた。

  • 純粋なクエリのレスポンスタイムを調べるため、クエリはCloudCodeから発行(モバイルなどのクライアントから打つとインターネット通信も含んでしまう。おそらくCloudCodeをアップロードする先はParseのDBと同じリージョンにあるはず)
  • 100件、1,000件、10,000件、100,000件、1,000,000件、10,000,000件のレコードを持つClassを作りそれぞれに対してクエリを発行する
  • データはランダムな文字列(randomString)を入れる。
  • objectId指定と、randomString指定でクエリを打ってみる
  • 10件クエリを打った平均時間を算出
  • データ挿入用、Select用のCloudCodeは末尾に記載


  1. レコード件数別レスポンスタイム
レコード件数 objectId指定(msec) randomString指定(msec)
100 53.3 57.8
1,000 52.7 52.4
10,000 60.9 66.7
100,000 68.6 78.5
1,000,000 52.8 56.5
10,000,000 72.8 652.1

100万件まではほとんどレスポンスタイムが劣化しない。
1000万件の場合でも、objectIdを指定すればほぼ劣化はしない。
iPhone(日本)からParseにアクセスした場合、500件くらいのレコードで大体1.5秒くらいかかったので全体的なレスポンス時間のほとんどがクエリ発行以外の部分(多分、日本-アメリカの通信時間)。
という事で、特に日本から使う場合、モバイルクライアントから何発もクエリを発行するよりは、CloudCodeを使ってAPI一発でデータをごっそりとってきた方がパフォーマンスが良いはず。

  1. 取得件数別レスポンスタイム
取得件数 レスポンスタイム(msec)
1 51
10 53
100 71.5
500 120
1,000 207.9

ちょっと意外だったけど、取得件数に依存してレスポンスタイムが増えるらしい。
Parseのドキュメントによると1000件以上は一気に取れないようなので、skipを入れてforで回すしか無い。

skipのパフォーマンスも調べてみようとしたところ、こんなエラーが。。。
"Skips larger than 10000 are not allowed"
これが一番クリティカルなんじゃ。。。
10000以上のレコードはページングで取得できないってことか。
基本的にページングみたいな事はやめた方が良いってことかな。
ちなみに、skipを10000してもパフォーマンス劣化は見られなかった。
*1

まとめると、
100万件くらいまではクエリのレスポンスは悪化しない。
日本から使うならクエリを同一拠点からまとめて発行できるCloudCode使う方が良い
取得件数に依存してレスポンスは悪化する
skipは10000までしか指定できない

という事で、これに合わせてデータベース設計していかないと駄目ですねー

exports.insert = function(req, res) {
    // 1000件入力
    // 無料プランで使っているので1000件以上を一気に突っ込むと上限に達してしまう
    var max = 1000;
    var Data = Parse.Object.extend("ClassName");

    var complete_count = 0;

    var sampling_data = new Array();

    for (var i = 0; i < max; i++) {
        var data = new Data();
        data.set("randomString", Math.random().toString(36).slice(10));
        data.save(null, {
            success: function() {
                complete_count++;
                if (complete_count == max) {
                  res.json({"ok":data});
                }
            },
            error: function(error) {
              res.json({"error":error});
            }
        });
    }
}
// reqのパラメータにはobjectid=XXXかrandomstring=YYYを指定する
exports.select = function(req, res) {
    var Data = Parse.Object.extend("ClassName");
    var query = new Parse.Query(Data);
    if (req.query.objectid) {
        query.equalTo("objectId", req.query.objectid);
    } else if (req.query.randomstring) {
        query.equalTo("randomString", req.query.randomstring);
    } else {
        res.json({"res":"no_param"});
    }
    var from_time = new Date().getTime();
    query.find({
        success: function(results) {
            var to_time = new Date().getTime();
            var diff = to_time - from_time;
            var result = results[0];
            res.json({"diff":diff, "res":result});
        },
        error: function(error) {
            res.json({"res":error});
        }
    });
}


[asin:B00G395OOE:detail]

*1:追記。そもそも10000件以上のレコードのページング自体が少ないとは思うもののどうしてもやりたい場合には、skipではなくcreatedAtでソートした後に最後の値でgreaterThanをしてあげると10000件以上ある場合でもページング的な事が出来るようです。createdAtはミリ秒単位なので1ミリ秒の違いも無くinsertされるようなデータがある場合にはアウトですけど。