はじめに
本記事では、ネットワーク上に配置したSQLiteのDBファイルで
WAL設定が危険な理由と、
代替となる安全な設定をまとめます。
SQLiteをネットワーク共有フォルダ上で運用しようとして
WALモードを設定したところ問題が発生したため、
設定を見直した際の記録です。
1. なぜネットワーク上のDBファイルでWALが危険なのか
WALモードの仕組みをおさらい
WAL(Write-Ahead Logging)モードは
書き込み内容を一時的に-walファイルに記録し、
キリのいいタイミングでDBファイルに反映する仕組みです。
この仕組みが正常に動作するためには
複数プロセス間で-shm(共有メモリ)ファイルを
リアルタイムに共有できる環境が必要です。
ネットワーク上では何が起きるか
ローカル環境(安全)
プロセスA ─┐
├─ 共有メモリ(-shm)を直接共有 ─ DBファイル
プロセスB ─┘
ネットワーク環境(危険)
PC-A ── ネットワーク ── 共有フォルダの-shmファイル ── DBファイル
PC-B ── ネットワーク ── ↑
ネットワーク遅延・キャッシュの不整合が発生する
| 問題 | 内容 |
|---|---|
| 共有メモリが共有されない |
-shmファイルはローカルの共有メモリを前提としており、ネットワーク越しには正常に機能しない |
| ロック機構が信頼できない | NFS・Samba等のネットワークファイルシステムではファイルロックの動作が保証されない |
| DBファイルの破損リスク | 複数PCが同時に書き込んだ場合にデータが壊れる可能性がある |
| キャッシュの不整合 | OSがファイルをキャッシュするため、他PCの書き込みがすぐに見えない場合がある |
SQLite公式ドキュメントでも、ネットワークファイルシステム上での
WALモードの使用は明確に非推奨とされています。
データ破損が発生した場合、修復が困難になります。
2. 今回の設定値と理由
以下の4つのPRAGMAを設定しました。
import sqlite3
conn = sqlite3.connect(r"\\server\share\data.db")
conn.execute("PRAGMA journal_mode = TRUNCATE;")
conn.execute("PRAGMA synchronous = FULL;")
conn.execute("PRAGMA locking_mode = NORMAL;")
3. journal_mode(ジャーナルモード)
データ破損に備えて変更内容を記録する方式を設定します。
PRAGMA journal_mode = TRUNCATE;
設定値一覧
| 設定値 | 内容 | ネットワーク上での安全性 |
|---|---|---|
DELETE |
変更をジャーナルファイルに記録し、コミット後に削除する | ◯ |
TRUNCATE |
コミット後にジャーナルファイルを削除せず0バイトに切り詰める | ◯(今回の設定) |
PERSIST |
コミット後もジャーナルファイルを残す(ヘッダのみ書き換え) | ◯ |
MEMORY |
ジャーナルをメモリ上のみに保持する(クラッシュ時にデータ消失) | △ |
WAL |
Write-Ahead Loggingを使用する | ✕ ネットワーク上では危険 |
OFF |
ジャーナルなし(ロールバック不可・最速だが危険) | ✕ |
TRUNCATEを選んだ理由
- WALを使わないためネットワーク上でも安全
- ジャーナルファイルの削除(DELETEモード)より
ファイルの切り詰めの方がI/Oコストが低く高速 - ファイルが残るためOS側のファイルキャッシュが再利用されやすい
4. synchronous(同期モード)
書き込みデータをどのタイミングでディスクに同期するかを設定します。
PRAGMA synchronous = FULL;
設定値一覧
| 設定値 | 内容 | 安全性 | 速度 |
|---|---|---|---|
OFF |
OSに任せて同期しない(最速・最も危険) | ✕ | 最速 |
NORMAL |
重要なタイミングのみ同期する | △ | 速い |
FULL |
すべての書き込みでディスクへの同期を保証する | ◎(今回の設定) | 遅い |
EXTRA |
FULLより厳密に同期する(ディレクトリも同期) | ◎ | 最も遅い |
FULLを選んだ理由
- ネットワーク環境では書き込みの途中でセッションが切れるリスクがある
-
FULLにすることで書き込みのたびにディスクへの同期を保証し
データ破損を防ぐ - 速度よりデータの安全性を優先する方針
ローカル環境ではNORMALが一般的なバランス設定です。
ネットワーク環境ではFULLを推奨します。
5. locking_mode(ロックモード)
DBファイルのロックをどのように管理するかを設定します。
PRAGMA locking_mode = NORMAL;
設定値一覧
| 設定値 | 内容 | ネットワーク上での挙動 |
|---|---|---|
NORMAL |
トランザクションごとにロックを取得・解放する(デフォルト) | ◎(今回の設定) |
EXCLUSIVE |
接続している間ずっとDBファイルを占有する | △ 他プロセスがアクセスできなくなる |
NORMALを選んだ理由
-
EXCLUSIVEはDBを占有し続けるため
複数PCからのアクセスができなくなる -
NORMALはトランザクション終了後にロックを解放するため
他のプロセスが順番にアクセスできる - ネットワーク環境での複数クライアントからの同時アクセスに対応するために必須
WALモードではEXCLUSIVEが実質必要になる場面が多く、
これもネットワーク環境でWALを避ける理由の一つです。
6. 設定のまとめと適用コード
設定値まとめ
| PRAGMA | 設定値 | 目的 |
|---|---|---|
journal_mode |
TRUNCATE |
WALを避けつつ安全に変更を記録する |
synchronous |
FULL |
書き込みのたびにディスクへの同期を保証する |
locking_mode |
NORMAL |
トランザクションごとにロックを解放する |
適用コード(Python)
import sqlite3
def get_connection(db_path: str) -> sqlite3.Connection:
"""
ネットワーク上のSQLiteに安全に接続する
Args:
db_path: DBファイルのパス(UNCパス等)
Returns:
設定済みのConnectionオブジェクト
"""
conn = sqlite3.connect(db_path)
conn.execute("PRAGMA journal_mode = TRUNCATE;")
conn.execute("PRAGMA synchronous = FULL;")
conn.execute("PRAGMA locking_mode = NORMAL;")
return conn
if __name__ == "__main__":
db_path = r"\\server\share\data.db"
conn = get_connection(db_path)
# 設定確認
print(conn.execute("PRAGMA journal_mode;").fetchone())
print(conn.execute("PRAGMA synchronous;").fetchone())
print(conn.execute("PRAGMA locking_mode;").fetchone())
conn.close()
8. ローカル環境との設定比較
| PRAGMA | ローカル推奨 | ネットワーク推奨 | 理由 |
|---|---|---|---|
journal_mode |
WAL |
TRUNCATE |
WALはネットワーク上で動作が保証されない |
synchronous |
NORMAL |
FULL |
ネットワーク越しの書き込み途切れに備える |
locking_mode |
NORMAL |
NORMAL |
変更なし |
まとめ
ネットワーク上のSQLiteでWALモードが危険な理由は
-shmファイルを使った共有メモリの仕組みが
ネットワークファイルシステムでは正常に機能しないためです。
代替設定として以下の組み合わせが有効です。
journal_mode = TRUNCATE → WALを使わず安全にジャーナルを管理
synchronous = FULL → 書き込みをディスクに確実に同期
locking_mode = NORMAL → 複数クライアントがアクセスできるよう都度ロック解放
ネットワーク環境でSQLiteを使う場合は
WALモードへの変更をしないよう注意してください。