[Tempest Python 深度解讀] Django 的 Async 不再只是附註:官方敘事改寫後,Python Web 架構該怎麼選 (2026-07-27)

Django 的非同步能力並非突然「完成」,真正改變的是官方如何描述它:從過去近似風險警告的附帶功能,轉為可在合適工作負載中正式採用的架構選項。這不是效能神話成真,也不代表既有專案應全面改寫;它更像是 Django 社群終於替同步、非同步與背景工作的邊界,畫出一張較可信的地圖。

事件背景:被舊文件壓低的成熟度

來源事實:Talk Python To Me 第 556 集訪問前 Django Fellow、現任 steering council 成員 Carlton Gibson。他參與重寫 Django 的 async 主題文件,主張舊版文字延續了功能初建時的保守態度,容易讓開發者誤以為整套機制仍不可用。節目同時討論 Django 6.0 Tasks、6.1 的 fetch modes 與資料庫層級刪除,以及 free-threaded Python。原始節目與逐字稿見 https://talkpython.fm/episodes/show/556/updates-on-djangos-async-story

官方 Django 6.0 文件交叉驗證了這項敘事轉變:Django 已支援 async view,若部署於 ASGI,可使用完整的非同步 request stack;ORM、快取、驗證、session 與 signal 等多個部分也提供非同步 API。async view 雖可在 WSGI 下執行,但會為每次請求建立一次性的 event loop,無法取得高效率長連線的主要好處。文件見 https://docs.djangoproject.com/en/6.0/topics/async/

運作機制:async 買的是等待期間的承載力

同步 worker 遇到資料庫、外部 API 或串流等待時,通常無法同時處理另一項請求;async 則讓 event loop 在 I/O 等待期間切換到其他工作。因此它改善的核心不是「每段 Python 程式都跑得更快」,而是同一個 process 能承擔更多尚未完成的 I/O。

合理推論:async 的商業價值應以「每台機器可維持多少連線、需要多少 worker、記憶體與尖峰延遲是否下降」衡量,而不是只看單一 API 的平均回應時間。節目中主持人分享減少 ASGI worker 後降低某個應用程式約 500 MB 記憶體的案例;這是具體經驗,但不能直接外推到所有 Django 系統。

真正適合 ASGI 的場景包括 WebSocket、Server-Sent Events、long polling、串流回應,以及同一請求需平行等待多個外部服務。反之,若請求主要只有一次普通 ORM 查詢與模板輸出,改成 async 未必有顯著收益。Django 官方也要求依自身程式進行 WSGI/ASGI 效能測試,而非預設 ASGI 必然較快。

為何現在值得注意

第一,官方文件已把 sync/async 轉接成本描述得更精確:ASGI 請求內重用既有 event loop 時,單次轉接約為數十微秒;冷啟動路徑則是數百微秒。相較常見的毫秒級請求,這通常不是主要瓶頸,但若在迴圈中反覆跨越邊界,成本仍會累積。

第二,Django 6.0 已加入 Tasks framework,統一定義、驗證、排隊與結果處理介面。官方同時明確指出:Django 不提供實際執行工作的 worker,內建 backend 主要供開發與測試,正式環境仍須外部程序或服務。它降低的是套件與應用程式綁死特定佇列產品的成本,不是消除背景工作基礎設施。文件見 https://docs.djangoproject.com/en/6.0/topics/tasks/

第三,開發中的 Django 6.1 把效能治理延伸到 ORM。官方 release notes 顯示,FETCH_PEERS 可在首次存取未載入欄位時,一次取得同一 QuerySet 的對應資料,把多數 N+1 情況降為兩次查詢;新的 DB_CASCADE 等選項則把刪除關聯交由資料庫執行,避免先把物件載入 Python。不過截至文件目前狀態,6.1 仍標示為開發版本、預計 2026 年 8 月發布,不能把預定功能當成已穩定交付。見 https://docs.djangoproject.com/en/dev/releases/6.1/

容易忽略的限制

來源事實:Django 6.0 的 transaction 尚不能直接在 async 模式運作;官方建議把完整交易包成單一同步函式,再透過 sync_to_async() 呼叫。async 模式也應停用 CONN_MAX_AGE,改用資料庫 backend 或第三方 connection pool。換句話說,event loop 可以承接大量等待中的請求,但真正同時執行的查詢仍受連線池與資料庫容量限制。

同步 middleware 夾在 ASGI server 與 async view 之間時,Django 必須切回 thread 執行,長連線的併發優勢可能因此被每請求一條 thread 抵銷。第三方套件是否支援 async,也比核心框架宣告更關鍵。至於 CPU-bound 工作,async 本身不會創造平行運算能力。

Free-threaded CPython 提供另一條路,但仍非免費升級。Python 3.14 官方文件指出,部分 C extension 尚未相容時可能重新啟用 GIL;free-threaded build 的單執行緒負擔依平台與工作負載約高出 1% 至 8%,且通常增加記憶體使用。見 https://docs.python.org/3/howto/free-threading-python.html

對台灣團隊的意義

作者觀點:對人力有限、同時承擔產品與維運的台灣 SaaS、電商或內部系統團隊,最合理的路線不是全面 async 化,而是從高等待、邊界清楚的端點開始。先修正慢 SQL、索引與 N+1,再針對外部 API 聚合、即時通知或 AI 串流建立 ASGI 路徑;其餘成熟的 CRUD 流程可繼續使用同步模型。節目提出以反向代理按路徑分流 WSGI 主站與 ASGI sidecar,也是一種可控制風險的過渡方案。

後續觀察指標

  • 同一流量下的 worker 數、RSS 記憶體、p95/p99 延遲與逾時率。
  • 同步/非同步邊界次數,以及第三方 middleware 被 Django 轉接的紀錄。
  • 資料庫連線池飽和度、慢查詢、N+1 次數與交易區段占比。
  • Django 6.1 最終 release notes、套件相容性,以及 free-threaded Python 是否會因 extension 自動恢復 GIL。

Django async 的關鍵進展,不是框架從此取代同步架構,而是團隊不必再把它視為禁區。成熟的選擇標準仍然樸素:先找出時間花在哪裡,再決定要增加資料庫效率、搬出背景工作,或讓等待中的連線共享同一個 event loop。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *