今天的好奇心解答主題是 Django的 request.session.get() 是否會造成性能下降? 🎯
在進行Django開發時,經常會多次調用 request.session.get('key_name')
。然而我突然有了這樣的思考。
"如果會話最終還是儲存在資料庫,那調用
request.session.get()
時是否會每次都發生資料庫查詢呢?""還是Django做了某種聰明的處理,因而不會發生額外的查詢?"
為了解決這個疑問,我深入研究了Django的 SessionMiddleware源代碼,並通過實驗來確認。 🚀
1️⃣ Django中的 request.session 是如何生成的?
我們來了解Django創建請求對象時會在何時載入會話。
我們通常在視圖函數中這樣獲取會話數據。
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的會話 | ❌ 無資料庫查詢 |
🚀 因此,使用cache
或signed_cookies
後端時,有可能在無資料庫查詢的情況下取得會話。 ✔ 對於性能重要的情況,考慮使用 CACHED_DB_SESSION
是一個好的選擇。
🎯 結論
- ✅ Django透過
在請求的初始階段然後就預先載入會話數據 ,將其存儲到request.session
物件中。 - ✅ 因此,在視圖中調用
request.session.get('username')
不會引發額外的資料庫查詢。 - ✅ 在模板中使用會話數據時也不會引發額外的資料庫查詢。
- ✅ 但是,當修改會話值時,Django會在發送響應時儲存更改的數據到資料庫。
- ✅ 根據Django的會話後端設定,查詢是否發生可能會有所不同,使用基於快取的(
cache
或cached_db
)可減少資料庫負擔。
🔥 下期預告: request
對象會在何時消失?
那麼 request
對象會在何時消失呢?
-
請求結束後仍然佔用記憶體嗎?
-
系統會自動清理嗎,還是我們需要手動整理呢?
🚀 在下期中,我們將探討Django的request
對象的生命週期以及記憶體管理! 敬請期待! 😃
Add a New Comment