Yoru Karu Studio

程式設計學習筆記 | 生活心得

Django 面試準備 11-3:競態條件處理

11-3. 競態條件處理(Race Condition Handling) 📌 什麼是競態條件? 簡單說: 多個線程同時操作共享數據,結果取決於執行順序 定義: 當兩個或多個線程並發訪問共享資源,且至少有一個線程進行寫操作時,如果沒有適當的同步機制,程序的行為將變得不可預測。 🔍 經典的競態條件示例 銀行轉帳問題 # ❌ 有競態條件的代碼 class BankAccount: def __init__(self, balance=0): self.balance = balance def withdraw(self, amount): # 步驟 1: 檢查餘額 if self.balance >= amount: # 步驟 2: 計算新餘額 new_balance = self.balance - amount # 步驟 3: 更新餘額 self.balance = new_balance return True return False account = BankAccount(balance=100)競態條件發生: 時間 Thread 1 (提款 80) Thread 2 (提款 80) balance T1 檢查: 100 >= 80 ✓ T2 檢查: 100 >= 80 ✓ 100 T3 計算: 100 - 80 = 20 T4 計算: 100 - 80 = 20 100 T5 寫入: balance = 20 T6 寫入: balance = 20 20 結果:兩次提款共 160 元,但餘額只減少 80 元!💥 正確應該是:餘額不足,第二次提款失敗 🎯 Django 中的常見競態條件 場景 1:庫存扣減 # models.

Django 面試準備 11-2:全局變量陷阱

11-2. 全局變量陷阱(Global Variable Pitfalls) 📌 為什麼全局變量危險? 簡單說: 多個線程共享同一個全局變量,會互相干擾 核心問題: 🔴 數據混淆:線程 A 的數據被線程 B 覆蓋 🔴 競態條件:並發修改導致數據不一致 🔴 難以調試:問題隨機出現,難以復現 🔴 陷阱 1:模塊級變量 問題演示 # views.py # ❌ 危險的全局變量 current_user = None def login_view(request): global current_user current_user = request.user # 設置全局變量 return HttpResponse(f"Logged in as {current_user.username}") def profile_view(request): global current_user # 期望獲取當前用戶 username = current_user.username return HttpResponse(f"Profile of {username}")問題場景 時間線: T1 Thread 1: login_view() current_user = User("Alice") T2 Thread 2: login_view() current_user = User("Bob") ← 覆蓋了! T3 Thread 1: profile_view() 讀取 current_user → 得到 User("Bob") ❌(期望 Alice) T4 Thread 2: profile_view() 讀取 current_user → 得到 User("Bob") ✓後果: Alice 看到了 Bob 的資料!嚴重的安全問題!

Django 面試準備 11-1:Django 線程安全機制

11-1. Django 線程安全機制(Thread Safety Mechanism) 📌 什麼是線程安全? 簡單說: 多個線程同時訪問同一資源時,不會出現數據錯誤或不一致 定義: 線程安全是指在多線程環境下,程序能夠正確地處理多個線程同時訪問共享資源,而不會導致數據競爭或不一致的狀態。 🔍 Django 的多線程環境 WSGI 服務器的線程模式 用戶請求 → WSGI 服務器 → Django 應用 WSGI 服務器(如 Gunicorn): ┌────────────────────────────────┐ │ Worker Process 1 │ │ ├─ Thread 1 → Request A │ │ ├─ Thread 2 → Request B │ │ └─ Thread 3 → Request C │ ├────────────────────────────────┤ │ Worker Process 2 │ │ ├─ Thread 1 → Request D │ │ ├─ Thread 2 → Request E │ │ └─ Thread 3 → Request F │ └────────────────────────────────┘ Django 請求處理流程 # 每個請求在獨立線程中處理 def handle_request(request): # 線程 1 Thread-1: 處理 Request A ├─ 創建 HttpRequest 對象 ├─ 執行中間件 ├─ 調用 View 函數 ├─ 渲染模板 └─ 返回 HttpResponse # 線程 2(同時進行) Thread-2: 處理 Request B ├─ 創建 HttpRequest 對象 ├─ 執行中間件 ├─ 調用 View 函數 ├─ 渲染模板 └─ 返回 HttpResponse關鍵: 每個請求都在獨立的線程中處理,互不干擾

Django 面試準備 10-4:查詢性能分析工具

10-4. 查詢性能分析工具(Query Performance Tools) 📌 為什麼需要性能分析工具? 問題: 如何發現查詢性能問題? 🔍 發現 N+1 查詢 📊 識別慢查詢 ⚡ 優化瓶頸 📈 監控性能 🔧 工具 1:Django Debug Toolbar 安裝配置 pip install django-debug-toolbar# settings.py INSTALLED_APPS = [ ... 'debug_toolbar', ] MIDDLEWARE = [ 'debug_toolbar.middleware.DebugToolbarMiddleware', ... ] INTERNAL_IPS = [ '127.0.0.1', ] # 可選配置 DEBUG_TOOLBAR_CONFIG = { 'SHOW_TOOLBAR_CALLBACK': lambda request: DEBUG, 'SQL_WARNING_THRESHOLD': 100, # 查詢時間超過 100ms 警告 } # urls.py from django.urls import include, path urlpatterns = [ .

Django 面試準備 10-3:索引優化技巧

10-3. 索引優化技巧(Database Index Optimization) 📌 什麼是索引? 簡單說: 索引就像書的目錄,幫助快速找到數據 定義: 數據庫索引是一種數據結構,用於加速數據查詢,就像書籍的目錄可以快速定位章節一樣。 🔍 索引的工作原理 沒有索引的查詢(全表掃描) -- 查詢用戶 SELECT * FROM users WHERE email = 'user@example.com'; -- 數據庫操作: 第 1 行: email = 'alice@example.com' → 不匹配 第 2 行: email = 'bob@example.com' → 不匹配 第 3 行: email = 'charlie@example.com' → 不匹配 ... 第 100萬行: email = 'user@example.com' → 匹配!✓ 時間複雜度: O(n) - 掃描所有行性能: 100 萬行數據 → 需要掃描 100 萬行 有索引的查詢(B-Tree 索引) B-Tree 索引結構: [m] / \ [c-l] [n-z] / \ / \ [a-f] [g-l] [m-r] [s-z] / \ [a-c] [d-f] / \ / \ [a][b][c][d][e][f] ↓ [user@example.

Django 面試準備 10-2:select_related vs Prefetch_related

10-2. select_related vs prefetch_related 📌 核心區別一覽 特性 select_related prefetch_related 適用關係 ForeignKey, OneToOneField ManyToManyField, 反向 ForeignKey SQL 策略 JOIN(單次查詢) 分別查詢後 Python 組合 查詢次數 1 次 通常 2 次 適合數據 少量關聯 大量關聯 SQL 複雜度 中等 簡單 內存使用 較低 較高 🎯 select_related 詳解 工作原理 核心: 使用 SQL JOIN 在一次查詢中獲取關聯對象 # models.py class Author(models.Model): name = models.CharField(max_length=100) class Post(models.Model): title = models.CharField(max_length=200) author = models.ForeignKey(Author, on_delete=models.CASCADE)基本用法 # ❌ 沒有優化:N+1 查詢 posts = Post.objects.all() for post in posts: print(post.
0%