今天的好奇心解答主題是 Django的 request.session.get() 是否會造成性能下降? 🎯

在進行Django開發時,經常會多次調用 request.session.get('key_name')。然而我突然有了這樣的思考。

"如果會話最終還是儲存在資料庫,那調用 request.session.get() 時是否會每次都發生資料庫查詢呢?"

"還是Django做了某種聰明的處理,因而不會發生額外的查詢?"

為了解決這個疑問,我深入研究了Django的 SessionMiddleware源代碼,並通過實驗來確認。 🚀

1️⃣ Django中的 request.session 是如何生成的?

我們來了解Django創建請求對象時會在何時載入會話。

Django session management process

我們通常在視圖函數中這樣獲取會話數據。

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

但是,如果每次使用 request.session.get('username') 時,Django都會發送資料庫查詢,那麼會對性能造成很大的影響。

🔍 來看看Django的內部!

其實Django通過名為SessionMiddleware的中介軟件,在請求進來時就預先載入會話數據。這一過程只會發生一次資料庫查詢,之後會將會話數據保留在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)僅從資料庫中獲取一次會話數據並儲存到request.session中
  • ✅ 換句話說,即使在視圖函數中多次調用 request.session.get(),也不會發生額外的資料庫查詢! 🎯

2️⃣ request.session.get() 會觸發資料庫查詢嗎?

現在回到關鍵問題。

"在視圖中調用 request.session.get('username') 會發生資料庫查詢嗎?"

🚀 結論是,即使調用 request.session.get() 也不會發生額外的資料庫查詢。

原因很簡單。
✔ Django在請求進來時會載入會話數據一次,並存放於 request.session 中。
✔ 隨後調用 request.session.get() 只是從已經載入的記憶體數據中獲取,因而不需要進行額外的資料庫檢索。

實際上,查詢資料庫以獲得會話的時刻僅在請求的初始階段發生一次。 此時執行的SQL查詢如下。

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

 換句話說,✅ 在視圖中多次調用 request.session.get(),Django不會執行額外的資料庫查詢!
      ✅ 從資料庫中拉取數據僅在請求初始時執行一次!

3️⃣ 在模板中使用會話數據時,也會發生查詢嗎?

"如果在視圖中查詢會話數據後,傳遞到模板中使用,這樣也會發生額外的資料庫查詢嗎?"

例如,假設在視圖中這樣傳遞會話數據。

def my_view(request):
    session_username = request.session.get('username')  # 會發生DB查詢嗎? ❌
    return render(request, 'my_template.html', {'session_username': session_username})

接著假設在模板中這樣使用。

<p>已登入作為: {{ session_username }}</p>

在這種情況下也不會發生額外的資料庫查詢!

session_username已經從 request.session 獲取的值,因此當Django將其傳遞給模板時不需要額外的資料庫查詢。

✔ Django將 request.session 管理如同記憶體快取,因此可以安全地在模板中使用。


4️⃣ 當修改會話數據時會儲存到DB嗎?

"那麼如果使用 request.session['username'] = 'new_value' 這樣修改數據呢?"

🚀 在這種情況下,資料庫的變更會被儲存,並伴隨著額外的資料庫查詢!

如果像下面這樣修改會話數據,

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

Django在發送響應時會將變更的會話數據儲存到資料庫。此時執行的 SQL 查詢如下。

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

讀取會話不會產生資料庫查詢,但是當更改會話時,Django會執行查詢以便儲存。


5️⃣ 會話儲存方式(後端)帶來的差異

Django允許改變儲存會話的方式(後端)。根據後端的不同,查詢是否會發生會有所不同

會話後端 描述 資料庫查詢是否發生
django.contrib.sessions.backends.db 基本的資料庫基礎會話 ✅ 請求時發生一次
django.contrib.sessions.backends.cache 基於快取的會話(Redis, Memcached) ❌ 無資料庫查詢
django.contrib.sessions.backends.cached_db 快取 + 資料庫會話(如果快取不存在則查詢資料庫) 🚀 快取不存在時發生
django.contrib.sessions.backends.signed_cookies 基於Cookie的會話 ❌ 無資料庫查詢

🚀 因此,使用cachesigned_cookies後端時,有可能在無資料庫查詢的情況下取得會話。 ✔ 對於性能重要的情況,考慮使用 CACHED_DB_SESSION 是一個好的選擇。


🎯 結論

  • ✅ Django透過在請求的初始階段然後就預先載入會話數據,將其存儲到 request.session 物件中。
  • ✅ 因此,在視圖中調用 request.session.get('username') 不會引發額外的資料庫查詢。
  • 在模板中使用會話數據時也不會引發額外的資料庫查詢。
  • ✅ 但是,當修改會話值時,Django會在發送響應時儲存更改的數據到資料庫。
  • ✅ 根據Django的會話後端設定,查詢是否發生可能會有所不同,使用基於快取的(cachecached_db)可減少資料庫負擔。

🔥 下期預告: request 對象會在何時消失?

那麼 request 對象會在何時消失呢?

  • 請求結束後仍然佔用記憶體嗎?

  • 系統會自動清理嗎,還是我們需要手動整理呢?

🚀 在下期中,我們將探討Django的request對象的生命週期以及記憶體管理! 敬請期待! 😃