SQLiteでWALモードを有効化して書き込み並行性とパフォーマンスを高める方法
SQLite Database Performance SQL
結論
全接続で busy_timeout を設定し、PRAGMA journal_mode=WAL; で並行性を向上させます。
-- SQLite公式仕様:並行性を向上させる PRAGMA 設定順序
-- 1. 接続直後にタイムアウト時間を設定 (コネクションごとに必須)
PRAGMA busy_timeout = 5000;
-- 2. WAL モードへ切り替え (DBファイルに永続保存)
PRAGMA journal_mode = WAL;
-- 3. 同期レベルの最適化 (必要に応じて)
PRAGMA synchronous = NORMAL;
SQLite公式仕様:従来ジャーナルと WAL モードの比較
SQLite公式ドキュメント(sqlite.org/wal.html)の規定通り、従来のロールバックジャーナルから WAL(Write-Ahead Logging)モードに変更することで動作モデルが根本的に変化します。
| 項目 | 従来モード (journal_mode=DELETE) | WAL モード (journal_mode=WAL) |
|---|---|---|
| 読み書き並行性 | ❌ 書き込み中は全読み取り(SELECT)がブロック | ◯ 複数の読み取りと1つの書き込みが同時実行可能 |
| I/O パフォーマンス | ❌ 変更ごとにディスク全書き換え | ◯ WALファイルへ追記するため超高速 |
| 永続性 | 接続ごとに毎回指定が必要 | ◯ DBファイルへ永続保存(初回1回でOK) |
実際に起こる障害:busy_timeout 設定漏れによる database is locked 事故
SQLiteでよくある勘違いが、PRAGMA journal_mode=WAL; さえ実行しておけば二度とロックエラーが起きないという誤解 です。
journal_mode=WALはDBファイルに永続記憶される が、PRAGMA busy_timeout=5000は接続(Connection)ごとの使い捨て(Ephemeral)設定である
そのため、新しいコネクションを張り直した際に busy_timeout を実行し忘れると、他のプロセスが書き込みを行っている最中にアクセスした瞬間、待機(Retry)を行わずに SQLITE_BUSY: database is locked エラーを即座に投げてアプリケーションがクラッシュする障害 が発生します。
実装手順
- データベース接続(Connection)を開く処理(Prisma, Better-SQLite3, Drizzle等)のフックに
PRAGMA busy_timeout = 5000;を組み込む - その後
PRAGMA journal_mode = WAL;を発行する - 本番環境の
busy_timeoutは 3000ms 〜 5000ms(3〜5秒)を確保し、一時的な書き込み競合を自動再試行で吸収させる