RAM 上に存在するデータベース
SQLite には :memory: という特別なファイル名があります。この名前でデータベースを開くと、SQLite はディスクをまったく使わず、データベース全体を RAM 上に展開します。テーブル、インデックス、トランザクション、外部キーといった機能はすべて通常どおり動作します。違うのは、コネクションを閉じた瞬間にデータベースが消える、という一点だけです。
コマンドラインから使う場合は次のようになります。
sqlite3 :memory:
これで SQLite のプロンプトに入った状態になりました。データベースはまっさらで、メモリ上にしか存在しません。テーブルを作って、行をいくつか入れて、クエリを投げる——いつもどおりの流れで OK です:
セッションを終了すれば、データはきれいさっぱり消えます。ファイルが残ることもありません。そもそもファイルが作られていないからです。
どんなときに使うのか
再起動で消えてしまうデータベースなんて、機能というよりバグでは?と思うかもしれません。でも、次の3つの場面では実際に役立ちます。
テスト用途。 テストごとにクリーンなデータベースをミリ秒単位で用意できます。一時ファイルの後片付けも、前回の実行で残った状態も、共有フィクスチャがロックされる心配もありません。Python・Node・GoのテストスイートでSQLiteを使う場合、:memory:を開いているのはまさにこの理由です。
使い捨ての分析。 CSVを読み込んで、クエリを投げて、捨てる。本物のデータベースを立ち上げるより速いし、毎回コードでファイルをパースするより楽です。
キャッシュやスクラッチ領域。 長時間動くプログラムの中では、SQLiteのインメモリデータベースは、すでに読み込み済みのデータに対して使うアドホックなクエリエンジンとして驚くほど優秀です。
共通しているのは、SQLは使いたいけど永続化はいらないということ。
パフォーマンス:速くはなるが、魔法ではない
インメモリデータベースはディスクを介さないので、本来ならファイルシステムに書き込まれるような操作も、ただのメモリ更新で済みます。I/Oがボトルネックのワークロードなら、はっきり速くなります。一方、CPUバウンドな処理 — 複雑なクエリプランニングや大規模なソートなど — はほとんど変わりません。SQLiteはもともとホットなページをメモリにキャッシュしているからです。
構文がまったく同じであることを、簡単に確かめてみましょう。
今のクエリはインメモリデータベース上で動かしましたが、ファイルベースのデータベースに対して投げる SQL とまったく同じです。エンジン側はどちらでも気にしません。
sqlite インメモリとファイル、どちらを選ぶか
トレードオフはシンプルなので、はっきり整理しておきましょう。
- ファイルデータベース(
mydata.db): 再起動しても残ります。複数のプロセスから開けます。クラッシュしてもだいたい生き残ります(WAL モード使用時)。何かを覚えておく必要があるなら、これ一択です。 - インメモリデータベース(
:memory:): 接続を閉じた瞬間に消えます。デフォルトでは、開いたコネクションだけがアクセスできます。書き込みの多い使い捨て処理では速いです。テスト・試し書き・短命なキャッシュ向き。
迷ったらファイルにしておきましょう。インメモリはあくまで特殊ケースです。
コネクションごとに別物のデータベースになる
地味にハマりやすいポイントですが、:memory: を 2 回開くと まったく別のデータベースが 2 つ できあがります。テーブルも共有されないし、データも見えないし、お互いの存在すら知りません。
-- ターミナル 1
sqlite3 :memory:
sqlite> CREATE TABLE t (x); INSERT INTO t VALUES (1);
-- ターミナル 2
sqlite3 :memory:
sqlite> SELECT * FROM t;
Error: no such table: t
これはバグではなく、仕様です。:memory: は「この接続専用のプライベートなデータベース」という意味なんですね。同じプログラム内でも同じことが起きます。:memory: への接続を2つ開けば、それぞれが独立した別々のデータベースを持つことになります。
SQLite インメモリデータベースを複数の接続で共有する
複数の接続から同じインメモリデータベースを参照したい場合、SQLite では URI 形式のファイル名と shared cache を使うことで実現できます。鍵となるのは file::memory:?cache=shared という文字列です:
sqlite3 'file::memory:?cache=shared'
同じプロセス内から同じ URI を開いた接続は、すべて同一のデータベースに接続されます。すべての接続をクローズした時点で、データベースは消えます。
複数の共有インメモリデータベースを使い分けたい場合は、名前付きインメモリデータベースを指定する方法もあります。
sqlite3 'file:mydb?mode=memory&cache=shared'
ここでの mydb という名前はあくまでラベルで、実体としてのファイルは存在しません。file:mydb?mode=memory&cache=shared を開いた2つの接続は同じデータベースを共有しますが、file:other?mode=memory&cache=shared を開いた接続は別のデータベースになります。
SQLite インメモリデータベースをディスクに保存する
メモリ上で一連の処理を済ませた後で、「やっぱり結果を残しておきたい」となる場面もあります。そんなときのために、CLI には .backup ドットコマンドが用意されています。
sqlite3 :memory:
sqlite> CREATE TABLE results (id INTEGER, score REAL);
sqlite> INSERT INTO results VALUES (1, 0.91), (2, 0.87);
sqlite> .backup snapshot.db
sqlite> .quit
snapshot.db がただのファイルデータベースとして保存され、中身もそのままです。あとから sqlite3 snapshot.db で開けば、中断したところから作業を続けられます。
逆方向もOKです。.restore を使えば、ファイルデータベースを現在の接続のメモリ上に読み込めます。
sqlite3 :memory:
sqlite> .restore snapshot.db
sqlite> SELECT * FROM results;
アプリケーション側のコードからは、SQLiteのC APIが sqlite3_backup_init と同じ仕組みをそのまま公開しており、ほとんどの言語バインディングはこれをラップしています。たとえばPythonの sqlite3 モジュールには Connection.backup() が用意されています。
よくある落とし穴
インメモリデータベースを「保存」しようとして、ファイルをATTACHしてコピーしようとする人がときどきいます。
単純なテーブルコピーならこれで十分ですが、インデックス・トリガー・ビュー・外部キーまで元通りには再現できません。データベース全体を忠実にコピーしたい場合は、.backup(あるいは backup API)を使いましょう。ページ単位でバイナリレベルの完全コピーが取れます。
ここまでのまとめ
:memory:は SQLite の特別なファイル名で、ファイルを持たない RAM 上のデータベースを作成します。- SQL の書き方はファイルベースのデータベースとまったく同じ。テーブルもクエリも制約もそのまま使えます。
:memory:への接続は接続ごとに独立しているので、複数の接続で共有したいときは共有キャッシュ URI(file::memory:?cache=shared)を使ってください。- 用途としてはテスト、使い捨ての分析、短命なキャッシュにぴったり。再起動後も残したいデータには向きません。
- 「やっぱり残したい」となったら、
.backupでメモリ上のデータベースをディスクに昇格できます。
次回:テーブルを作る
ここまでのサンプルでも CREATE TABLE をちらほら使ってきました。次のページではペースを落として、カラム定義・型・付けられる制約、そして後から効いてくる細かな設計判断まで、じっくり解説していきます。
よくある質問
SQLite でインメモリデータベースを作るには?
ファイルパスの代わりに特殊なファイル名 :memory: を渡して開くだけです。CLI なら sqlite3 :memory:、各言語のライブラリでも connect の引数に :memory: を指定すれば OK。データベースは RAM 上に作られ、コネクションを閉じた瞬間に消えます。
:memory: とは何ですか?
:memory: は SQLite が「ファイルを使わず全部メモリ上に置いて」という意味で認識する特別なファイル名です。テーブル・インデックス・トランザクションなど通常の SQLite の機能はすべて使えますが、ディスクには一切書き込まれません。:memory: を開いた各コネクションは、それぞれ独立したプライベートな DB を持ちます。
複数のコネクションでインメモリDBを共有できますか?
デフォルトでは無理です。:memory: で開いたコネクションは互いに独立しています。共有したい場合は file::memory:?cache=shared のような URI 形式で開き、shared cache を有効にします。同一プロセス内で同じ URI を開いたコネクションは、すべて同じ DB を参照します。
インメモリDBをディスクに保存できますか?
できます。CLI なら .backup コマンド、ライブラリなら backup API を使ってインメモリDBをファイルにコピーする方法が一番手軽です。あるいは、ファイルDBを ATTACH してから INSERT INTO file.table SELECT * FROM main.table でデータを流し込む方法もあります。