今日の好奇心解決テーマは Djangoのrequest.session.get()がパフォーマンス低下を引き起こすのか? です! 🎯

Django開発をしていると request.session.get('key_name')を何度も呼び出すことが多いですよね? しかし、ふとこんなことを考えました。

"セッションも結局DBに保存されるなら、 request.session.get()を呼び出すたびにDBクエリが発生するのか?"

"それともDjangoが何か賢い処理をしていて追加のクエリが発生しないのか?"

この疑問を解決するため、Djangoの SessionMiddlewareソースコードを深く掘り下げ、実験を通じて確認しました。 🚀

1️⃣ Djangoでrequest.sessionはどのように生成されるのか?

Djangoがrequestオブジェクトを作成する際、セッションをいつロードするのかを見てみましょう。

Django session management process

私たちは通常 ビュー関数でこのようにセッションデータを取得します。

session_username = request.session.get('username')

しかし request.session.get('username')を使用するたびにDjangoが DBクエリを行うのであれば、パフォーマンスに大きな影響を与える可能性がありますよね?

🔍 Django内部を見てみよう!

実際、Djangoは SessionMiddlewareというミドルウェアを通じてリクエストが入るときに セッションデータを事前にロードしています。このプロセスでDBクエリが 一度だけ発生し、その後は requestオブジェクト内にセッションデータを保持します。

Djangoの SessionMiddleware コードを見ると次のような処理が行われています。

class SessionMiddleware(MiddlewareMixin):
    def process_request(self, request):
        session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
        request.session = self.SessionStore(session_key)

📌 ここで重要なポイント!

  • request.COOKIESからセッションキー(session_key)を取得する。
  • self.SessionStore(session_key)を呼び出して、セッションデータを 一度だけDBから取得しrequest.sessionに保存する。
  • ✅ つまり、 ビュー関数でrequest.session.get()を何度呼び出しても追加のDBクエリは発生しない! 🎯

2️⃣ request.session.get()はDBクエリを発生させるのか?

では、核心の質問に戻りましょう。

"ビューでrequest.session.get('username')を呼び出すとDBクエリが発生するのか?"

🚀 結論を言うと、request.session.get()を呼び出しても追加のDBクエリは発生しません。

理由は簡単です。
✔ Djangoは リクエストが来たときにセッションデータを一度ロードした後、request.sessionに保存します。
✔ その後request.session.get()を呼び出すことは すでにメモリにロードされたデータを取得することなので追加のDB検索は必要ありません。

実際にDBでセッションを検索する瞬間はリクエストの最初だけです。 この時実行されるSQLクエリは次の通りです。

SELECT session_data FROM django_session WHERE session_key = 'xyz123' LIMIT 1;

 つまり、✅ ビューでrequest.session.get()を何度も呼んでもDjangoは追加のDBクエリを実行しない!
      ✅ DBからデータをロードするのはリクエストが最初に発生した時に一度だけ実行される!

3️⃣ テンプレートでセッションデータを使うときもクエリが発生するのか?

"ビューでセッションデータを取得した後、テンプレートに渡して、テンプレートでそれを使うときも追加のDBクエリが発生するのか?"

例えば、ビューでセッションデータを次のように渡すとしましょう。

def my_view(request):
    session_username = request.session.get('username')  # DB検索発生? ❌
    return render(request, 'my_template.html', {'session_username': session_username})

そしてテンプレートでこのように使います。

<p>Logged in as: {{ session_username }}</p>

この場合にも追加のDBクエリは発生しません!

session_usernameすでにrequest.sessionから取得した値なので、Djangoがこれをテンプレートに渡すときに追加のDB検索は必要ありません。

✔ Djangoは request.sessionメモリキャッシュのように管理するため、テンプレートでも安全に使用できます。


4️⃣ セッションデータを変更するとDBに保存されるのか?

"では request.session['username'] = 'new_value'のようにデータを変更した場合は?"

🚀 この場合、DBに変更が保存されて追加のDBクエリが発生します!

もし次のようにセッションデータを変更したら、

request.session['username'] = 'new_value'

Djangoは応答を送る際に 変更されたセッションデータをDBに保存します。このとき実行されるSQLクエリは次の通りです。

UPDATE django_session SET session_data = 'new_value' WHERE session_key = 'xyz123';

セッションを読み取ることはDBクエリを発生させませんが、セッションを変更するとDjangoがこれを保存するためにDBにクエリを実行します。


5️⃣ セッション保存方式(バックエンド)による違い

Djangoではセッションを保存する方法(バックエンド)を変更することができます。バックエンドによって クエリ発生の有無が異なることに注意が必要です。

セッションバックエンド 説明 DBクエリ発生の有無
django.contrib.sessions.backends.db 基本DBベースのセッション ✅ リクエスト時に一度発生
django.contrib.sessions.backends.cache キャッシュベースのセッション(Redis、Memcached) ❌ DBクエリなし
django.contrib.sessions.backends.cached_db キャッシュ + DBセッション(キャッシュがなければDB検索) 🚀 キャッシュにない場合に発生
django.contrib.sessions.backends.signed_cookies クッキーベースのセッション ❌ DBクエリなし

🚀 つまり、cachesigned_cookiesバックエンドを使用すればDBクエリなしでセッションを取得することも可能です。 ✔ パフォーマンスが重要な場合 CACHED_DB_SESSIONを考慮することも良い方法です。


🎯 結論

  • ✅ Djangoは リクエストの初期にSessionMiddlewareを通じてセッションデータを事前にロードしてrequest.sessionオブジェクトに保存します。
  • ✅ 従って、 ビューでrequest.session.get('username')を呼び出すことは追加のDBクエリを発生させません。
  • テンプレートでセッションデータを使用する際にも追加のDBクエリは発生しません。
  • ✅ しかし、 セッションの値を変更するとDjangoが応答を送る際にDBに変更されたデータを保存します。
  • ✅ Djangoのセッションバックエンド設定によって クエリ発生の有無が異なるため、キャッシュベース(cacheまたはcached_db)を使用すればDBの負担を減らすことができます。

🔥 次回予告:requestオブジェクトはいつ消えるのか?

それではrequestオブジェクトはいつ消えるのでしょうか?

  • リクエストが終わってもメモリを占有し続けるのでしょうか?

  • システムが自動で掃除してくれるのか、それとも私たちが直接整理しなければならないのか?

🚀 次回はDjangoのrequestオブジェクトの寿命とメモリ管理について探求してみます! お楽しみに! 😃