ブラウザストレージ(localStorageとIndexedDB)のパフォーマンスを比較する
はじめに
ブラウザウィンドウ間でデータ共有を実装しようとすると、ブラウザストレージを使うという手段があります
今回は比較的扱いやすいlocalStorageとIndexedDBを取り上げて、CRUD(Create/Read/Update/Delete)のインターフェースと簡単なパフォーマンス計測を比較しようと思います
CRUD
データ保存、取得、削除の方法をlocalStorageとIndexedDBでそれぞれ見ていきます
localStorageのCRUD
ここで紹介するまでもないですが以下のような感じになります
セッター、ゲッターなどのメソッドはいくつかありますが一例を紹介します
// Create/Update
localStorage.setItem('id', 1);
// Read
localStorage.getItem('id');
// Delete
localStorage.removeItem('id');
プリミティブではない値(ArrayやObject)を扱う場合は、
localStorageに保存するときJSON.stringify
でJSON→string
取り出す時に文字列で保存されているのでJSON.parse
でstring→JSONする必要があります
const json = { id: 1, name: "kthatoto" };
const stringJson = JSON.stringify(json);
//=> '{"id":1,"name":"kthatoto"}'
localStorage.setItem('profile', stringJson);
const stringJson = localStorage.getItem('profile');
//=> '{"id":1,"name":"kthatoto"}'
const json = JSON.parse(stringJson);
//=> { id: 1, name: "kthatoto" }
IndexedDBのCRUD
今回はDexie.jsを使います
IndexedDBを簡単に扱えるライブラリです
まずはDBのセットアップをします
import Dexie, { Table } from "dexie";
interface Profile {
name: string;
email: string;
content: string;
}
export class Database extends Dexie {
simpleTable!: Table<{ id: number, value: number }>;
profiles!: Table<{ id: number, profile: Profile }>;
constructor() {
super('BrowserStorageComparison');
this.version(1).stores({
simpleTable: 'id',
profiles: 'id',
});
}
}
export const db = new Database();
DBにアクセスしたいファイルから、上記でexportしたものをimportして各種メソッドを呼び出すことで使えます
基本的に非同期メソッドなので注意が必要です
import { db } from "./database";
// Create
await db.simpleTable.add({ id: 1, value: 1 });
// Update
await db.simpleTable.put({ id: 1, value: 1 });
// Read
await db.simpleTable.get(1);
// Delete
await db.simpleTable.delete(1);
CRUDの比較
IndexedDB(Dexie)は初期設定こそ少し手間ですがそのあとはlocalStorageと比べても同じような手軽さでデータの操作ができます
(StringとJSONの変換をしなくていい分こちらの方が楽まである)
また上記のようにDatabaseクラスを定義することで型が効くのでこの点においてはlocalStorageに対する明確なアドバンテージといえます
パフォーマンス計測
1000回読み書きする時間を計測します
localStorageの計測
const startTime = performance.now();
for (let i = 0; i < 1000; i++) {
const profile = {
id: i,
profile: {
name: "kthatoto",
email: "kthatoto@example.com",
content: "ああああああああああああああ",
},
};
localStorage.setItem(`id_${i}`, JSON.stringify(profile));
JSON.parse(localStorage.getItem(`id_${i}`)!);
}
const endTime = performance.now();
const time = endTime - startTime;
console.log(`${time}ms`);
localStorageの計測結果
10回計測の平均を出します
9ms
9.799999952316284ms
10.099999904632568ms
10.700000047683716ms
10.700000047683716ms
10.900000095367432ms
10.800000190734863ms
7.8999998569488525ms
7ms
8.200000047683716ms
AVG: 9.510000014305115ms
IndexedDBの計測
const startTime = performance.now();
for (let i = 0; i < 1000; i++) {
const profile = {
id: i,
profile: {
name: "kthatoto",
email: "kthatoto@example.com",
content: "ああああああああああああああ",
},
};
await db.profiles.put(profile);
await db.profiles.get(i);
}
const endTime = performance.now();
const time = endTime - startTime;
console.log(`${time}ms`);
IndexedDBの計測結果
3126.2999999523163ms
3042.699999809265ms
3133.399999856949ms
3082ms
3052.2000000476837ms
2898.9000000953674ms
2954.2000000476837ms
3044.2000000476837ms
3048.0999999046326ms
3014.9000000953674ms
AVG: 3039.689999985695ms
パフォーマンス計測結果の比較
約300倍の違いがありました
localStorage | IndexedDB |
---|---|
9.51ms | 3039.69ms |
localStorageの読み書きが圧倒的に速いという結果になりました
IndexedDBの使いどころ
パフォーマンス計測結果だけ見てみるとlocalStorage一択ということになりそうですが、
以下のような状況のときはIndexedDBを選択する余地はあります
- 単純に扱いたいデータ容量がlocalStorageの最大容量を超える時
- 型が効くので複雑な構造をしたデータを扱いたいとき
- DBインデックスによる高速検索を活用したい時
おわりに
IndexedDBを色々使っていてlocalStorageよりパフォーマンスが良いと感じたので実際に計測してみようという動機で始めましたが予想と全く逆の結果となってしまいました
おそらく1つのDBテーブルに当たるものをlocalStorageで1つのキーで配列として管理していたことにより同時読み書きがうまくいかなかったことによるものな気がしています
localStorageで items
というキーにitemsテーブルに相当するものを全て入れるのではなく item_1
item_2
...のようにキーを分けて管理することでlocalStorageをうまく扱えるようになるかもしれないですが検証はまたいつか
Discussion