SQLiteの基礎 — ローカルアプリがなぜ「ファイル1つのDB」を選ぶのか
ブラウザやスマートフォンのアプリ、デスクトップのツールの中には、データの保存先に SQLite を使っているものが多い。MySQL や PostgreSQL のような「サーバーとして動くデータベース」とは違い、SQLite は1つのファイルがそのままデータベースになる。
この記事では、SQLite が何者なのかを、Python 標準ライブラリの sqlite3 での小さな実験で確かめながら整理する。特定の製品の実装ではなく、一般的な性質の解説である。
補足: 数値は、2026年10月14日に手元の Python 3(SQLite 3.45.3)で実行した結果。環境によって変わる。
SQLiteとは何か
SQLite は、アプリの中に組み込んで使うデータベースエンジンである。MySQL のように別のプロセスを立ち上げて接続する必要がなく、アプリが直接ファイルを読み書きする。
違いを整理すると次のようになる。
| 項目 | サーバー型(MySQL 等) | SQLite |
|---|---|---|
| 動き方 | 別プロセスとして常駐し、接続して使う | アプリのプロセスの中でファイルを直接操作する |
| データの置き場所 | サーバー側のデータ領域 | 1つのファイル |
| 設定 | ユーザー・権限・接続情報が必要 | ファイルのパスだけ |
| 向く場面 | 多数の利用者が同時に書き込む | 1つのアプリが自分のデータを持つ |
WordPress は MySQL 系のサーバー型を使う。複数の訪問者が同時にアクセスするためである。一方、1台の PC の中で1つのアプリが自分のデータだけを扱うなら、サーバーを立てる手間は、たいてい割に合わない。
実験1: ファイルを作ってみる
一時フォルダに、テーブルを1つ作ってデータを入れた。
import sqlite3
c = sqlite3.connect("t.db")
c.execute("create table log(id integer primary key, msg text)")
1000行を入れたあとのフォルダの中身は t.db の1ファイルだけで、サイズは32,768バイト(32KB)だった。このファイルをコピーすれば、データベースのコピーになる。バックアップや持ち運びが単純なのは、この性質のおかげである。
ただし、書き込み中のファイルをそのままコピーすると、途中の状態を拾う可能性がある。コピーは、アプリが書き込んでいないときに行うか、SQLite の備える専用のバックアップ機能を使うほうが安全である。
実験2: コミットの回数で、時間はどう変わるか
同じ1000行の挿入を、2通りで比べた。
| 方法 | 所要時間 |
|---|---|
| 1行ごとにコミット(1000回) | 約0.29秒 |
| 1回のトランザクションにまとめて、最後にコミット | 約0.0007秒 |
差は400倍ほどだった。SQLite はコミットのたびに、データが確実にファイルへ書かれたことを確かめる。その確認の回数が、時間の大半を占める。
多くの行を入れるときは、まとめて1つのトランザクションにするのが基本である。逆に言えば、1行ごとにコミットするコードは、動いていても遅い。
実験3: 同時に書こうとするとどうなるか
SQLite は、同時に書き込めるのは1つの接続だけという性質を持つ。確かめるため、1つ目の接続で書き込みの準備(begin immediate)を取ったまま、2つ目の接続から同じことを試した。0.1秒待っても取れず、次のエラーになった。
OperationalError: database is locked
これは故障ではなく、「いま別の誰かが書いている」という正常な応答である。対処は次のように考える。
- 待つ時間(タイムアウト)を設定する。短すぎると、少し混み合っただけでエラーになる
- 書き込みのトランザクションを短くする。長く握るほど、他が待たされる
- 書き込みが頻繁で、しかも多数の接続が必要なら、サーバー型を検討する
この制約が、SQLite が「1つのアプリが自分のデータを持つ」用途に向くと言われる理由である。
ジャーナルモードとWAL
SQLite は、書き込みの途中で電源が落ちても壊れにくいように、変更の記録を別に残す。この方式をジャーナルモードと呼ぶ。
既定は delete モードで、実験でも pragma journal_mode は delete を返した。これを WAL モードに切り替えると、フォルダには t.db-wal と t.db-shm という2つのファイルが加わった。
WAL モードでは、読み取りが書き込みを待たず、書き込みも読み取りを待たないようになる。読み取りが多いアプリでは効果が出やすい。ただし、次の点に注意する。
- データベースが「1ファイル」ではなく3ファイルの組になる。コピーや移動のときは、3つを揃えて扱う必要がある
- ネットワーク越しの共有フォルダ上では、WAL は使えない
型が「ゆるい」という性質
SQLite は、列に型を書いても、型を厳密には強制しない。実験では、integer と宣言した列に文字列 'abc' を入れてみたところ、エラーにならず、そのまま text として保存された。
これは柔軟さでもあり、落とし穴でもある。入力の検証はアプリ側で行う前提で使う。厳密にしたいなら、STRICT テーブルの指定や CHECK 制約を使う方法がある。
気をつけたい4つの点
1. ネットワーク共有の上に置かない
複数の PC から共有フォルダ上の同じ SQLite ファイルを開く使い方は、ファイルのロックが期待どおりに働かない場合があり、破損の原因になりやすい。1つのファイルは、1台の機械の中のアプリが使うのが基本である。
2. 書き込みはまとめる
実験2のとおり、コミットの回数は速度に直結する。
3. バックアップは「動いていない瞬間」か専用機能で
ファイルのコピーは手軽だが、書き込み中は避ける。WAL モードなら3ファイルの整合も気にする。コピーしたあとは、ハッシュ値で同じ内容か確かめる習慣をつけておくと安心である。
4. 多数が同時に書くなら、別の道具を選ぶ
SQLite は優れた道具だが、万能ではない。同時書き込みが多いサービスには、サーバー型のほうが向く。
まとめ
- SQLite は、1つのファイルがそのままデータベースになる、組み込み型のエンジンである。設定が少なく、コピーや持ち運びが単純
- 1000行の挿入は、1行ずつコミットで約0.29秒、まとめて1回で約0.0007秒。書き込みはトランザクションにまとめる
- 同時に書けるのは1接続だけ。別の接続が書いていると
database is lockedになる。タイムアウトの設定と、短いトランザクションで対処する - WAL モードは読み書きの待ちを減らすが、ファイルが3つの組になる
- 型は厳密に強制されない。検証はアプリ側で行う
ローカルで動くアプリが「ファイル1つのDB」を選ぶのは、サーバーを立てずに済み、データの持ち運びが単純だからである。その代わり、同時書き込みの少なさと、ファイルの扱い方に気を配る前提で使う。同じく「同じ処理を重ねても壊れない」設計に関心があれば、冪等性(idempotency)とはも参照してほしい。