コンテンツへスキップ

SQLiteの基礎 — ローカルアプリがなぜ「ファイル1つのDB」を選ぶのか

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)とはも参照してほしい。