はじめに
FastAPIとSQLAlchemyでデータベース接続を設定しているとき、こんなコードを見たことはありませんか?
engine = create_engine(
settings.DATABASE_URL,
connect_args={"check_same_thread": False} # SQLite only
)
この check_same_thread=False って何?なぜSQLiteだけ必要なの?
この記事では、前提知識から丁寧に解説していきます。
この記事で学べること
- スレッドとは何か
- Webサーバーがどのように動くか
- SQLiteと他のデータベースの違い
- なぜ
check_same_thread=Falseが必要なのか
1. まず「スレッド」を理解しよう
スレッドとは?
スレッド = プログラム内で同時に動く作業単位です。
レストランのキッチンでたとえてみましょう。
【シングルスレッド(料理人1人)】
注文A → 調理 → 完成 → 注文B → 調理 → 完成
(1つずつ順番に処理)
【マルチスレッド(料理人3人)】
注文A → 料理人1が調理 ─┐
注文B → 料理人2が調理 ─┼─ 同時進行!
注文C → 料理人3が調理 ─┘
マルチスレッドだと複数の処理を同時にできるので効率的です。
2. Webサーバー(FastAPI)の動き
ユーザーがWebサイトにアクセスすると「リクエスト」が発生します。
ユーザーA → リクエスト「ユーザー一覧見せて」
ユーザーB → リクエスト「商品を購入したい」
ユーザーC → リクエスト「ログインしたい」
FastAPIは複数のリクエストを同時に処理するため、内部でスレッドを使います。
リクエストA → スレッド1 が担当
リクエストB → スレッド2 が担当
リクエストC → スレッド3 が担当
FastAPIは非同期処理も使いますが、同期的なDB操作ではスレッドプールが使われることがあります。
3. データベース接続とは?
プログラムがデータベースを使うには、まず「接続」を確立します。
プログラム ←――― 接続 ―――→ データベース
(パイプのようなもの)
この接続を通じて「データをください」「データを保存して」とやり取りします。
4. SQLiteと他のDBの決定的な違い
SQLiteの特徴
SQLiteはファイル1つで動く軽量データベースです。
| 項目 | SQLite | PostgreSQL / MySQL |
|---|---|---|
| 構成 | ファイル1つ(database.db) |
サーバーとして常駐 |
| セットアップ | 不要 | サーバー構築が必要 |
| 複数アクセス | 制限あり | 想定済み |
| 用途 | 開発・小規模 | 本番・大規模 |
SQLiteの安全機構
SQLiteは軽量な反面、スレッドに関する安全機構があります。
「この接続はスレッドAで作ったから、スレッドAだけが使ってね」
これにより、マルチスレッドでの不整合やデータ破損を防いでいます。
5. 問題が起きる状況
FastAPIとSQLiteを組み合わせると、以下のような問題が発生します。
① サーバー起動時にDB接続を作成(スレッドAで作成)
↓
② ユーザーからリクエストが来る
↓
③ FastAPIがスレッドBでそのリクエストを処理
↓
④ スレッドBが①の接続を使おうとする
↓
⑤ SQLite「それスレッドAで作った接続でしょ?ダメ!」
↓
💥 エラー発生!
実際のエラーメッセージ:
sqlite3.ProgrammingError: SQLite objects created in a thread
can only be used in that same thread.
6. 解決策:check_same_thread=False
engine = create_engine(
settings.DATABASE_URL,
connect_args={"check_same_thread": False}
)
この設定をすると、SQLiteに「スレッドのチェックをしないで」と伝えます。
【デフォルト(check_same_thread=True)】
スレッドA で接続作成
├→ スレッドA からアクセス → ✅ OK
└→ スレッドB からアクセス → ❌ エラー
【check_same_thread=False】
スレッドA で接続作成
├→ スレッドA からアクセス → ✅ OK
└→ スレッドB からアクセス → ✅ OK(チェック無効化)
7. 他のデータベースでは不要
PostgreSQLやMySQLは、最初からマルチスレッドを想定して設計されています。
そのため、check_same_thread のような設定は存在しません。
# PostgreSQLの場合
engine = create_engine("postgresql://user:pass@localhost/dbname")
# → check_same_threadは不要(そもそも存在しない)
8. 本番環境での対応
開発環境ではSQLite、本番環境ではPostgreSQLを使うことが多いです。
その場合は、条件分岐で設定を切り替えましょう。
# 環境に応じて設定を切り替える
if "sqlite" in settings.DATABASE_URL:
connect_args = {"check_same_thread": False}
else:
connect_args = {}
engine = create_engine(settings.DATABASE_URL, connect_args=connect_args)
まとめ
| 観点 | SQLite | PostgreSQL / MySQL |
|---|---|---|
| スレッド対応 | 制限あり | 対応済み |
check_same_thread |
必要(Falseに設定) | 不要 |
| 用途 | 開発・テスト | 本番環境 |
check_same_thread=False はSQLiteの安全機構を無効化するため、本番環境では適切なデータベース(PostgreSQL等)への移行を推奨します。