接続プールって何?まずはイメージから
接続プール(コネクションプール)は、
「データベースへの接続を、使い回すための“待機列(プール)”」です。
DB への接続は、実はかなり重い処理です。
毎回「接続を開く → クエリする → 接続を閉じる」をやっていると、
接続を開くコストだけでアプリがどんどん遅くなります。
そこで、
あらかじめ何本か接続を作っておく
使うときはプールから 1 本借りる
使い終わったら閉じずにプールに返す
という仕組みが「接続プール」です。
「毎回新しく作る」のではなく「作っておいたものを再利用する」ことで、
パフォーマンスと安定性を両方上げるための仕組みだと思ってください。
なぜ接続を毎回作ると遅くなるのか
DB 接続は「重いネットワーク処理」
例えば、PostgreSQL や MySQL に接続するとき、内部ではこんなことが起きています。
サーバーとの TCP 接続を張る
認証(ユーザー名・パスワード・権限チェック)を行う
セッションの初期化(設定・トランザクションモードなど)を行う
これらは全部「そこそこ重い処理」です。
1 回だけなら気になりませんが、
Web アプリでリクエストごとに毎回新規接続していたら、
「接続を張るだけの時間」が積み重なって、レスポンスがどんどん重くなります。
「接続を開く・閉じる」を繰り返すコードのイメージ
例えば、こんなコードを想像してください。
import psycopg2
def get_user(user_id: int):
conn = psycopg2.connect("postgres://...")
cur = conn.cursor()
cur.execute("SELECT id, name FROM users WHERE id = %s", (user_id,))
row = cur.fetchone()
cur.close()
conn.close()
return row
Pythonこの関数を 1 秒間に何十回・何百回も呼ぶと、
毎回 connect() が走り、そのたびに接続確立のコストがかかります。
「クエリ自体は軽いのに、接続のせいで遅い」
という、もったいない状態になりがちです。
接続プールの基本的な考え方
「接続を再利用する」という発想
接続プールは、発想をこう変えます。
必要になるたびに新しく接続を作る
→ あらかじめ接続をいくつか作っておいて、使い回す
イメージとしては、
プールの中に「接続オブジェクト」が何本か並んでいる
アプリが「接続が欲しい」と言うと、プールから 1 本貸し出される
使い終わったら「close」ではなく「プールに返却」される
別のリクエストが来たら、その返却済み接続をまた使う
という流れです。
これにより、
接続確立のコストを何度も払わなくて済む
同時に使える接続数を制御できる(DB に負荷をかけすぎない)
というメリットが得られます。
SQLAlchemy での接続プールのイメージ
Engine が接続プールを持っている
SQLAlchemy では、create_engine を呼んだときに、
裏で「接続プール」が自動的に用意されます。
from sqlalchemy import create_engine
engine = create_engine(
"postgresql+psycopg2://user:password@localhost:5432/appdb",
echo=True,
)
Pythonこの engine が、接続プールを内蔵しています。
Session や engine.connect() を通してクエリを投げるとき、
実際にはこのプールから接続が貸し出され、使い終わるとプールに戻されます。
つまり、SQLAlchemy を普通に使っているだけで、
「接続プールあり」の世界にすでに乗っている、ということです。
プールのサイズや挙動を指定する
接続プールの設定は、create_engine の引数で変えられます。
engine = create_engine(
"postgresql+psycopg2://user:password@localhost:5432/appdb",
pool_size=5,
max_overflow=10,
pool_timeout=30,
pool_recycle=1800,
)
Pythonここでよく使うのは次のようなイメージです。
pool_size
プールに常に保持しておく接続の本数(例:5 本)
max_overflow
pool_size を超えて、一時的に増やしてよい接続の上限(例:+10 本)
pool_timeout
プールから接続を借りようとして空きがないとき、何秒待つか
pool_recycle
接続を何秒ごとに作り直すか(長時間使いっぱなしによる問題を避けるため)
Web アプリでは、「同時に何リクエストくらい来るか」「DB がどれくらい耐えられるか」を考えながら、
このあたりの値を調整していきます。
Web アプリでの接続プールの使われ方イメージ
「1 リクエスト=1 セッション」+「セッションの裏に接続プール」
FastAPI や Flask などで SQLAlchemy を使うとき、
よくあるパターンはこうです。
Engine(接続プール付き)をアプリ全体で 1 個作る
リクエストごとに Session を 1 個作る
Session は内部で Engine の接続プールから接続を借りる
リクエスト処理が終わったら Session を閉じる(接続はプールに返る)
コードのイメージはこんな感じです。
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine("postgresql+psycopg2://...", pool_size=5, max_overflow=10)
SessionLocal = sessionmaker(bind=engine)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
Pythonここで db.close() と書いていますが、
実際には「接続を完全に破棄」ではなく「プールに返却」です。
つまり、
アプリ全体で接続プールを共有しつつ
各リクエストは「セッション」という単位で DB と会話する
という二段構造になっています。
接続プールがないとどうなるか(極端な例)
毎回新規接続する Web アプリを想像してみる
もし接続プールを使わず、
リクエストごとに毎回 connect() して close() していたらどうなるか。
1 秒間に 100 リクエスト来る
そのたびに DB への新規接続が 100 回発生する
DB サーバーは接続確立と認証でヘトヘトになる
アプリ側も接続待ちでレスポンスが遅くなる
さらに、DB 側には「同時接続数の上限」があります。
PostgreSQL なら max_connections、MySQL なら max_connections などです。
接続プールなしで無制限に connect() しまくると、
この上限にぶつかって「これ以上接続できません」というエラーになります。
接続プールは、
接続の再利用で速くする
同時接続数を制御して DB を守る
という 2 つの役割を同時に果たしてくれます。
接続プールで意識しておきたいポイント
「プールを増やせばいい」は罠
「遅いなら pool_size を増やせばいいんでしょ?」
と考えたくなりますが、これは半分正解で半分罠です。
プールを増やすと、
同時に処理できるリクエスト数は増える
でも DB にかかる負荷も増える
というトレードオフがあります。
DB サーバーの CPU・メモリ・ディスク I/O が耐えられる範囲を超えてしまうと、
逆に全体が遅くなったり、タイムアウトが増えたりします。
だから、接続プールのサイズは、
アプリ側の同時リクエスト数
DB サーバーのスペック
他のサービスとの兼ね合い
を見ながら、少しずつ調整していくのが現実的です。
「プールを使っている前提」でコードを書く
SQLAlchemy を使うときは、
基本的に「Engine はアプリ全体で 1 個」「Session は短命」という設計にします。
毎回 create_engine していると、
そのたびに新しい接続プールができてしまい、
プールの意味が薄れてしまいます。
よくあるアンチパターンはこれです。
def get_user(user_id: int):
engine = create_engine("postgresql+psycopg2://...")
SessionLocal = sessionmaker(bind=engine)
session = SessionLocal()
user = session.query(User).get(user_id)
session.close()
Pythoncreate_engine を関数の中で呼んでしまうと、
呼ぶたびに新しいプールが作られます。
正しくは、engine と SessionLocal はモジュールレベル(アプリ起動時)で 1 回だけ作り、
リクエストごとに SessionLocal() でセッションだけを作る、という形にします。
まとめ(接続プールは「DB 接続を賢く共有するための土台」)
接続プールを初心者目線で整理すると、こうなります。
DB への接続は重いので、毎回新しく作るのではなく、「あらかじめ作っておいた接続をプールして再利用する」仕組みが接続プール。
SQLAlchemy の create_engine は、デフォルトで接続プールを持っていて、Session や engine.connect() はそのプールから接続を借りて使う。
Web アプリでは「Engine(プール)はアプリ全体で 1 個」「リクエストごとに Session を作って、終わったら閉じてプールに返す」という設計が基本。
プールサイズを増やせばいいわけではなく、DB の限界や他サービスとのバランスを見ながら調整する必要がある。
