本文へスキップ
ウェブエンジニア問題集
第11章

SQLiteの運用 — バックアップ・VACUUM・PRAGMA設定

約11分
この章の目次開く

SQLiteの運用は、他のデータベースに比べて驚くほど単純です。監視すべきプロセスはなく、バージョンアップの調整も要らず、バックアップは1つのファイルを保存するだけで済みます。

ただし「ファイルをコピーするだけ」が成立するのは、アプリが動いていないときに限られます。稼働中のデータベースを安全に扱うには、専用の手段を使う必要があります。

学習者学習者

1ファイルなんだから、cp でコピーすればバックアップ完了だと思っていました。

先生先生

止まっているならそれで正しい。動いている最中だと、書き込みの途中の状態をコピーしてしまうことがあるんだ。

なぜ cp が危険なのか

cp はファイルを先頭から順に読みます。読んでいる最中にアプリが別の場所を書き換えると、コピー先には「前半は書き換え前、後半は書き換え後」という、どの時点にも存在しなかった状態が残ります。

さらに 第6章で見たWALモードでは、最新の変更が -wal ファイル側にあります。本体ファイルだけをコピーすると、直近のコミットがまるごと失われます。

稼働中のSQLiteを cp でバックアップしてはいけません。壊れたファイルができるか、直近のデータが欠けます。

VACUUM INTO — 一貫したスナップショットを作る

もっとも手軽で確実な方法が VACUUM INTO です。SQLite 3.27.0 以降で使えます。

構文: VACUUM INTO 'コピー先のファイルパス'

VACUUM INTO '/backup/app-2026-08-15.db';
sql

この命令は、実行時点の一貫した状態を新しいファイルへ書き出します。アプリが動いたままでも安全で、しかも書き出しの過程で断片化が解消されるため、元より小さいファイルになります。

sqlite3 app.db "VACUUM INTO '/backup/app-$(date +%Y%m%d).db'"
bash

シェルから1行で実行できるため、cronに登録するだけで日次バックアップが完成します。

sqlite3 コマンドの .backup も同様に安全な方法です。こちらはWALファイルも含めて整合性を保ちます。

sqlite> .backup /backup/app.db
方法稼働中の安全性断片化の解消備考
cp / rsync危険なし停止中のみ可
VACUUM INTO安全ありSQL1行。cron向き
.backup安全なしCLIのドットコマンド
ファイル3つをまとめてコピー不確実なし推奨されない
選択するイメージ
日次のスナップショットならVACUUM INTO、継続的な保護なら次章のレプリケーションを検討します

VACUUM — ファイルを整理する

VACUUM(INTO なし)は、データベースファイルそのものを再構築します。

VACUUM;
sql

大量の DELETE を実行しても、SQLiteのファイルサイズは自動では小さくなりません。空いた領域は「フリーリスト」として再利用のために保持されるだけです。VACUUM はこれを解放し、ファイルを実際に縮めます。

どのくらい無駄があるかは、次のクエリで確認できます。

SELECT
  page_count * page_size / 1024 / 1024 AS total_mb,
  freelist_count * page_size / 1024 / 1024 AS free_mb
FROM pragma_page_count(), pragma_page_size(), pragma_freelist_count();
sql
日常的に VACUUM を実行する必要はありません。大量削除の後や、ファイルサイズが実データに対して明らかに大きいときだけで十分です。

自動で少しずつ整理させたい場合は auto_vacuum を使います。

PRAGMA auto_vacuum = INCREMENTAL;
sql
値挙動
NONE(既定)何もしない。手動 VACUUM が必要
FULLコミットのたびに空き領域を返す。書き込みが遅くなる
INCREMENTAL空き領域を記録しておき、明示的に指示したときだけ解放する

INCREMENTAL を選んだ場合、実際の解放は次の命令で行います。

PRAGMA incremental_vacuum(100);  -- 100ページ分だけ解放
sql

破損していないか確認する

SQLiteのファイルが壊れることは稀ですが、第6章で触れたネットワークファイルシステムでの利用や、ディスク障害では起こり得ます。

PRAGMA integrity_check;
sql

問題がなければ ok の1行が返ります。全ページを検証するため、大きなデータベースでは時間がかかります。

PRAGMA quick_check;
sql

こちらは検証項目を絞った簡易版で、はるかに高速です。定期チェックには quick_check、異常を疑ったときには integrity_check という使い分けが実用的です。

外部キーの整合性は別の命令です。第4章で見た通り、外部キーが無効な状態で投入されたデータには違反が残っている可能性があります。

PRAGMA foreign_key_check;
sql

実務で設定しておきたいPRAGMA

ここまでの章で登場した設定をまとめます。ファイルに保存されるものと接続ごとに必要なものを区別するのが重要です。

PRAGMA保存先推奨値効果
journal_modeファイルWAL読み取りと書き込みを並行できる
auto_vacuumファイルINCREMENTAL空き領域を管理する
busy_timeout接続5000ロック待ちでリトライする
foreign_keys接続ON外部キー制約を有効にする
synchronous接続NORMALWAL環境での速度と安全性のバランス
cache_size接続-20000ページキャッシュを20MBに
temp_store接続MEMORY一時テーブルをメモリに置く
-- 初回のみ(ファイルに保存される)
PRAGMA journal_mode = WAL;
 
-- 接続ごとに実行する
PRAGMA busy_timeout = 5000;
PRAGMA foreign_keys = ON;
PRAGMA synchronous = NORMAL;
PRAGMA cache_size = -20000;
PRAGMA temp_store = MEMORY;
sql

接続を閉じる直前には、第7章で触れた統計情報の更新を入れておきます。

PRAGMA optimize;
sql
PRAGMA optimize は必要と判断したときだけ処理を行う軽量な命令です。接続クローズ時に毎回呼んで構いません。
前に進むイメージ
接続時と切断時に決まったPRAGMAを流すだけで、運用上の問題の多くを予防できます

WALファイルの管理

第6章で触れた通り、WALファイルは放置すると肥大化することがあります。上限を設けておくと安心です。

PRAGMA journal_size_limit = 67108864;  -- 64MB
sql

これは、チェックポイント後にWALファイルをこのサイズまで切り詰めるという設定です。手動で完全に切り詰めたい場合は次を実行します。

PRAGMA wal_checkpoint(TRUNCATE);
sql

バックアップ戦略を決める

用途によって、必要な保護のレベルは変わります。

状況推奨する方法
個人ツール・CLI週次で VACUUM INTO、ファイルをどこかに保存
社内ツール・読み取り中心のWebアプリ日次 cron で VACUUM INTO + オブジェクトストレージへ転送
データを失いたくない本番サービス継続的レプリケーション(次章)
開発環境バックアップ不要。マイグレーションから作り直す
#!/bin/sh
# 日次バックアップの例
set -eu
DEST="/backup/app-$(date +%Y%m%d).db"
sqlite3 /var/lib/app/app.db "VACUUM INTO '$DEST'"
gzip "$DEST"
find /backup -name 'app-*.db.gz' -mtime +30 -delete
bash
バックアップは「取れていること」ではなく「戻せること」を確認して初めて完成します。復元手順を一度は実際に試してください。

よくあるハマりどころ

バックアップから復元できるか試していない

ファイルは毎日保存されているのに、復元したら壊れていた、というのはよくある事故です。取得したファイルに対して PRAGMA integrity_check を実行するところまでを、スクリプトに含めておきましょう。

sqlite3 "$DEST" "PRAGMA integrity_check" | grep -q '^ok$' || exit 1
bash

VACUUM をトランザクションの中で実行しようとする

VACUUM はトランザクションの内側では実行できません。また、未完了のトランザクションが他にある状態でも失敗します。

WALファイルを消せばきれいになると思う

-wal ファイルには、まだ本体に反映されていない変更が入っています。アプリが動いている状態で削除すると、データが失われます。切り詰めたい場合は必ず wal_checkpoint を使ってください。

PRAGMAを設定したつもりで効いていない

journal_mode 以外のほとんどのPRAGMAは接続ごとの設定です。ORMやコネクションプールを使っている場合、新しい接続では初期値に戻っています。接続確立時のフックで必ず実行されているかを確認してください。

現在の値は、値を書かずに実行すれば確認できます。

PRAGMA busy_timeout;
PRAGMA foreign_keys;
sql

ちゃんと使うためのポイント

  • 稼働中のデータベースを cp でバックアップしない
  • VACUUM INTO 'ファイル' が最も手軽で安全なスナップショット方法
  • VACUUM はファイルを縮めるが、ロックと一時領域が必要。日常的には不要
  • PRAGMA quick_check を定期チェック、integrity_check を異常時に使う
  • PRAGMAは「ファイルに保存されるもの」と「接続ごとのもの」を区別する
  • 接続時に WAL / busy_timeout / foreign_keys / synchronous、切断時に optimize
  • バックアップは復元まで一度試して初めて完成する

最終章では、ここまで前提としてきた「1台のマシンの中で完結する」という制約を超える方法を扱います。Turso・libSQL・Cloudflare D1・Litestream といった選択肢と、SQLiteを本番に採用してよい場面の判断基準を整理します。

参考リンク

SQLクイズに挑戦するデータベース運用の知識を、4択クイズでアウトプットして定着させよう