MobileSafari における Web SQL Database の実質利用可能な容量を調べてみた

MobileSafari の Web SQL Database では最大 50 MB の容量が利用可能です。これはデータベースを初期化する際に(初めて openDatabase を実行する際に) 25 MB 以上のサイズを指定すると最大 50 MB の容量を使用することを許可するか確認するダイアログが出て、50 MB を超える値を指定するとエラーになることから確認できます。1

っで、この 50 MB は DB を利用する側にとって実質どの程度使えるのか知りたかったので調べてみました。検証端末は iPhone 4S (iOS 5.1.1) です。2

検証コード

キーとバリューを持つようなレコードをエラーになるまで挿入するコードです。
各レコード、キー・バリュー合わせて 1 kB, 2 kB, … 1024 kB になる場合でそれぞれ調べました。
——- 2012/8/14 追記 ——-
JavaScript は ASCII 文字も含め全ての文字に関して 16 bit のメモリを消費するみたいなので、2 kB, 4 kB, …, 2048 kB になっています。

var db = openDatabase("test", "", "", 50 * 1024 * 1024);

var createTable = function(table) {
    db.transaction(
        function(tx) {
            tx.executeSql("CREATE TABLE IF NOT EXISTS " + table + " (key UNIQUE, value)");
        },
        function(error) {
            console.error("error: " + error.message);
        },
        function() {
            console.log("success: create table " + table);
        }
    );
};

var dropTable = function(table) {
    db.transaction(
        function(tx) {
            tx.executeSql("DROP TABLE IF EXISTS " + table);
        },
        function(error) {
            console.error("error: " + error.message);
        },
        function() {
            console.log("success: drop table " + table);
        }
    );
};

var repeatStr = function(str, n) {
    return new Array(n + 1).join(str);
};

var main = function(size) {
    var table = "test";
    dropTable(table);
    createTable(table);

    var key = repeatStr("0", 24);          // 24 bytes
    var str = repeatStr("0", 1024);        // 1 kB
    var value = repeatStr(str, size - 1);  // (size-1) kB
    value += repeatStr("0", 1000);         // (size-1) kB + 1000 bytes

    var nextKey = function(key) {
        key++;
        return repeatStr("0", 24 - (key + "").length) + key;
    };

    var insertData = function(key, value) {
        db.transaction(
            function(tx) {
                tx.executeSql("INSERT OR REPLACE INTO " + table + " (key, value) VALUES (?, ?)", [key, value]);
            },
            function(error) {
                console.error("size: " + size + "kB, key: " + key);
                console.error("error: " + error.message);
                if (size > 1) {
                    main(size / 2);
                }
            },
            function() {
                //console.log("success: insert " + key );
                insertData(nextKey(key), value);
            }
        );
    };
    insertData(key, value);
};

main(1024);

結果

1レコード 1 kB の場合のみが特殊で、あとは全レコードのサイズが約 25 MB に到達するところでエラーになるみたいです。
レコードの内容はランダムな文字列の方が適切かもしれませんが、画像を base64 エンコードした値を挿入する場合の容量上限と感覚的にずれはないので妥当な結果に思われます。
——- 2012/8/14 追記 ——-

1レコードのサイズは1文字1バイトで計算しているので、実際は2倍のサイズです

1レコードのサイズ(kB) 挿入可能なレコード数
1 12554
2 11200
4 5968
8 3085
16 1568
32 790
64 396
128 198
256 98
512 48
1024 23

ちなみに 1 kB の場合はこちらでも検証されていて、同じような結果になっています。

ところで、実際に複数のレコードを挿入する場合は今回のように1つ前のレコードの挿入が成功した場合のコールバック関数で次のレコードを挿入することはないと思いますが、 その場合コンソール画面を表示した状態だと容量制限を超えても成功した場合のコールバック関数しか呼ばれず、一見挿入が成功したように見えるので注意が必要です。これを知らないとハマります。

試しに main 関数の最後の内容を次の内容に変えて、コンソール画面を表示した上で実行すると 1024 kB のデータが 1000 件挿入できたように見えます。全ての処理が終わるまでコンソール画面を表示しなければ 24 件目以降エラーメッセージが出続けます。エラーメッセージが出てきたところでコンソール画面を表示するとログ上は再び挿入に成功し出します。

    var insertData = function(key, value) {
        db.transaction(
            function(tx) {
                tx.executeSql("INSERT OR REPLACE INTO " + table + " (key, value) VALUES (?, ?)", [key, value]);
            },
            function(error) {
                console.error("size: " + size + "kB, key: " + key);
                console.error("error: " + error.message);
            },
            function() {
                console.log("success: insert " + key );
            }
        );
    };

    for (var i = 0; i < 1000; i ++) {
        insertData(key, value);
        key = nextKey(key);
    }

以上、MobileSafari の Web SQL Database で実質利用可能な容量を調べました。「MobileSafari では約 2550 MB までデータが保存できる」と覚えておくと良いかもしれません。あと、小さなデータを大量に保存する場合には向かないかもしれませんね。

  1. 初期化の際に小さなサイズを指定しても実際の利用状況が 5 MB を超える時点で最大 10 MB、10 MB を超える時点で最大 25 MB、25 MB を超える時点で 最大 50 MB の容量を使用することを許可するか確認するダイアログが出ます 

  2. 実はもっと前に1レコード 100 kB になるようにして調べたことがあったのですが、何故か1文字 2 bytes で計算していたので再度検証した感じです・・・