10,000 個內部員工用,一切都很順。
百萬外部用戶第一天上線,系統在 30 分鐘內崩潰。
「加更多機器」不是答案——
正確的問題是:哪些地方讓你根本無法加機器?
面試情境
面試官:「你幫一家金融公司做了內部員工 AI 助手,10,000 個內部用戶,系統很穩定。現在 CEO 決定把這個產品開放給外部客戶,目標是百萬 MAU(月活躍用戶)。你說需要重新設計架構。從哪裡開始?你會做哪些改動?為什麼?」
一、為什麼 10K → 1M 不只是「加機器」
10K 內部用戶的隱性假設(這些假設在 1M 時全部失效):
用戶行為:
├── 行為模式可預測(9-18 點工作時間,流量曲線平滑)
├── 用量相對均勻(員工配額相似,不會有人瘋狂濫用)
└── 系統問題可以容忍(內部用戶有耐心,可以接受偶爾慢)
系統設計:
├── Session State 在記憶體(少數實例,重啟少)
├── 認證:單一 LDAP/SSO(一種身份系統就夠)
├── 沒有速率限制(員工不會惡意攻擊自家系統)
└── SLA:P95 < 10s 內部用戶接受
1M 外部用戶:每一個假設都被打破
假設失效 真實挑戰 系統症狀
──────────────────────────────────────────────────────────────
行為可預測 病毒式傳播:1 小時內 100x 流量 Auto-Scale 來不及 → 503
用量均勻 惡意用戶濫用、失控的客戶端 Bug 一個用戶拖垮整個平台
Session 在記憶體 Scale-Out 後新實例找不到 Session 對話斷掉,用戶流失
無速率限制 機器人、爬蟲、Bug 迴圈呼叫 LLM 配額耗盡 → 全平台崩潰
SLA 寬鬆 外部客戶不等待,直接離開 用戶留存率崩潰
成本不計較 1M × 50 queries × $0.02 = $1M/月 CFO 叫停
三個結構性差距(加機器解決不了):
差距 1:Stateful 設計 → Auto-Scale 加了機器但 Session 找不到,白加
差距 2:無速率限制 → 單一惡意用戶可以耗盡整個系統的 LLM 配額
差距 3:無非同步架構 → 長任務佔滿連接槽,新請求全部 503
二、三個演進階段的完整架構設計
╔══════════════════════════════════════════════════════════════╗
║ Phase 1:< 10K 用戶(POC / 內部試點) ║
║ 策略:速度優先,接受架構妥協 ║
╚══════════════════════════════════════════════════════════════╝
Client
│
▼
Load Balancer(Round-robin)
│
▼
Agent Service(Stateful,Session 存記憶體)
├──→ LLM API
├──→ Vector DB
└──→ PostgreSQL(Primary,讀寫混合)
✅ 可接受的妥協:
├── Session 在記憶體(重啟少,可接受)
├── 無速率限制(內部用戶信任)
└── 單一 DB(流量低,夠用)
❌ 擴展瓶頸:
├── 加第二台 Agent → Session 在 A,請求打到 B → 對話斷掉
└── DB 讀寫混合在高峰期互相干擾
新增元件:零 | 月成本:$300-800
╔══════════════════════════════════════════════════════════════╗
║ Phase 2:10K - 200K 用戶(MVP / 生產試點) ║
║ 策略:無狀態化 + 讀寫分離 + 基本快取 ║
╚══════════════════════════════════════════════════════════════╝
Client
│
▼
Load Balancer(Stateless,任意請求 → 任意實例)
│
▼
Agent Pool(無狀態,Auto-Scale on CPU + Concurrency)
├──→ Redis(Session + Tool 結果快取,TTL 管理)
├──→ LLM API(+ Exact-match Response Cache)
├──→ Vector DB
└──→ PostgreSQL
├── Primary(寫入)
└── Read Replica × 2(查詢,稽核,讀取)
✅ 解決的問題:
├── Scale-Out 有效(Session 在 Redis,哪個實例都能讀)
└── DB 讀寫分離(Read Replica 吸收 80% 讀取壓力)
❌ 殘留瓶頸:
├── 無語意快取(重複語義查詢仍打 LLM)
└── 長任務佔連接槽(10s 的任務佔住連接等 LLM)
新增元件:Redis Cluster、Read Replica × 2
月成本增量:+$500-1,200
╔══════════════════════════════════════════════════════════════╗
║ Phase 3:200K - 1M+ 用戶(Enterprise Scale) ║
║ 策略:非同步 + 語意快取 + 多層防護 + 成本控制 ║
╚══════════════════════════════════════════════════════════════╝
Client
│
▼
CDN(靜態資源快取 + 不含個人化的 API 回應快取)
│
▼
API Gateway(三層 Rate Limiting:Global / Tenant / User)
│
├──→ [同步路徑] 預估 < 8s 的即時請求
│ │
│ ▼
│ ┌─────────────────────────────────────────┐
│ │ Semantic Cache Layer │
│ │ Query → Embedding → Vector 相似度搜尋 │
│ │ 相似度 > 0.95 → 直接命中,< 100ms 回傳 │
│ └─────────────────┬───────────────────────┘
│ │(未命中)
│ ▼
│ Agent Pool(Auto-scale,基於 Queue Depth + P95)
│ ├──→ Redis(Multi-layer 快取)
│ ├──→ LLM API(Token Budget 管理)
│ └──→ Vector DB(Clustered,Multi-replica)
│
└──→ [非同步路徑] 預估 > 8s 的長任務
│
▼
Task Queue(Pub/Sub)
└── HTTP 202 立刻回傳 task_id,不佔連接槽
│
▼
Worker Pool(獨立 Auto-scale,基於 Queue Depth)
│
▼
Result Store(DB)
│
▼
通知機制(WebSocket Push 或 Polling Endpoint)
✅ 解決所有問題:
├── 語意快取命中率 40-60%,LLM 成本降低 40-60%
├── 非同步佇列消除長任務的連接槽瓶頸
└── 三層 Rate Limiting 防惡意用戶
新增元件:CDN, API Gateway, Semantic Cache, Task Queue, Worker Pool
月成本增量:+$2,000-5,000(但 LLM 成本降低 40-60%,淨效益正)
三、無狀態服務設計
問題核心:為什麼 Stateful 服務讓 Scale-Out 失效
Stateful 服務的 Scale-Out 悖論:
Phase 1 架構(Stateful):
Round 1:
User A → LB → Instance A(Session 存在 A)
User A → LB → Instance A(同一個實例,正常)
Scale-Out 觸發,新增 Instance B:
Round 2:
User A → LB → Instance B(LB 換了實例)
→ 找不到 Session → 對話重置
← 用戶體驗:「剛才說的話全忘了」
這就是加機器讓體驗更差的原因。
Stateless 服務設計:
┌──────────────────────────────────────────────────────────────┐
│ 請求攜帶 session_id(JWT Token 或 Cookie) │
└──────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Load Balancer(Pure Round-robin,任意請求 → 任意實例) │
└──────┬────────────────────────────────┬─────────────────────┘
│ │
▼ ▼
Instance A(無任何用戶 State) Instance B(無任何用戶 State)
│ │
└────────────────┬───────────────┘
│ 根據 session_id 讀取
▼
┌──────────────┐
│ Redis │
│ session_id │
│ → State │
└──────────────┘
任意實例都能讀取任意用戶的 State
→ Scale-Out 有效
→ Rolling Deploy 不中斷對話
→ 多 Region 部署可共用 Redis Cluster
Session State 分層設計:
State 類型 儲存位置 TTL 理由
──────────────────────────────────────────────────────────────
Conversation Redis 24h 高頻讀寫,低延遲
History
──────────────────────────────────────────────────────────────
User Profile PostgreSQL 永久 結構化,需要 JOIN 查詢
──────────────────────────────────────────────────────────────
Tool 結果快取 Redis 5-60min 依 Tool 的資料新鮮度
──────────────────────────────────────────────────────────────
Long-running PostgreSQL 7 天 需要持久化,可以跨 Session
Task Status
──────────────────────────────────────────────────────────────
四、非同步佇列架構
問題:同步架構的連接槽瓶頸
同步模型的計算:
10,000 並發 × 8s(平均延遲) = 需要 80,000 個同時持有的 HTTP 連接
Cloud Run 預設每個實例 concurrency=80 → 需要 1,000 個實例
LLM 配額 1,000 RPM → 只能支撐 1,000/60 ≈ 17 個並發
瓶頸不在計算資源,在 LLM 配額。加機器沒用。
非同步架構:把「等待」和「計算」分開
任務分類決策(在 API Gateway 層判斷):
判斷標準 路徑 理由
──────────────────────────────────────────────────────────────
預估 < 8s 同步 用戶期待即時回應
預估 8s-60s 非同步 + Push 後台處理 + WebSocket 推送
預估 > 60s 非同步 + Poll 長任務,用戶可以等
批次處理 非同步佇列 不需要即時回應
非同步流程完整設計:
┌──────────────────────────────────────────────────────────────┐
│ 用戶提交長任務(「分析這份 100 頁 PDF」) │
└──────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ API Gateway │
│ 1. 生成 task_id(UUID) │
│ 2. 寫入 Task Queue(Pub/Sub) │
│ 3. 立刻回傳 HTTP 202 Accepted + task_id │
│ (整個過程 < 50ms,不佔連接槽) │
└──────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Task Queue(訊息持久化,Worker 故障不丟失) │
│ 訊息:{task_id, user_id, payload, created_at} │
└──────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Worker Pool(獨立 Auto-scale,基於 Queue Depth) │
│ ├── Queue Depth > 100 → 擴展 Worker │
│ ├── 執行任務:LLM 呼叫、向量搜尋、結果整合 │
│ └── 完成後更新 Task Status = COMPLETED,寫入 Result │
└──────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 通知機制(兩種模式,依場景選擇) │
│ 模式 A:WebSocket Push(適合 Web App) │
│ 任務完成 → Server push 通知 → 用戶介面更新 │
│ 模式 B:Polling(適合 Mobile / 簡單場景) │
│ 用戶每 5s 呼叫 GET /tasks/{task_id} 查詢狀態 │
└──────────────────────────────────────────────────────────────┘
Task State Machine:
PENDING → PROCESSING → COMPLETED
→ FAILED(支援 Retry,最多 3 次)
→ TIMEOUT(超過 10 分鐘未完成)
Task Status 提供給用戶的資訊:
{
"task_id": "uuid-xxx",
"status": "PROCESSING",
"progress": 0.6, // 60% 完成
"estimated_completion": "30s",
"created_at": "...",
"updated_at": "..."
}
五、語意快取策略
為什麼傳統 Key-Value 快取在 AI 系統效果差:
傳統快取(Exact-match Key-Value):
Key = hash(query_string)
「今天天氣如何」→ hash_A → Cache Miss
「今天的天氣是什麼」→ hash_B → Cache Miss(兩次都打 LLM)
語意快取(Semantic Cache):
Key = vector_embedding(query)
「今天天氣如何」→ vector_A
「今天的天氣是什麼」→ vector_B
cosine_similarity(vector_A, vector_B) = 0.97 > 0.95 → Cache Hit ✅
語意快取的完整架構:
┌──────────────────────────────────────────────────────────────┐
│ User Query(自然語言) │
└──────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Embedding Model(將 Query 向量化,dim=768 或 1536) │
│ 延遲:10-30ms │
└──────────────────────┬───────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ 向量快取 DB(儲存歷史 Query 的向量 + 對應回應) │
│ 搜尋:ANN(Approximate Nearest Neighbor) │
│ 延遲:5-20ms │
│ │
│ 相似度 > 0.95 → Cache Hit → 回傳快取回應(< 60ms 總延遲) │
│ 相似度 ≤ 0.95 → Cache Miss → 執行完整 LLM 呼叫(2-8s) │
│ LLM 呼叫完成後 → 將 (query_vector, response) 存入快取 │
└──────────────────────────────────────────────────────────────┘
相似度閾值的選擇:
閾值 Cache Hit 率 風險
──────────────────────────────────────────────────────────────
0.99 低(5%) 低(幾乎完全相同才命中)
0.95 中(30-50%) 低(語義幾乎相同) ← 推薦
0.90 高(60-70%) 中(語義相近但可能不完全同)
0.85 很高(70%+) 高(可能回傳語義相近但不準確的快取)
三層快取整合效益:
層次 快取對象 命中率 節省
──────────────────────────────────────────────────────────────
Semantic Cache 自然語言查詢的語義匹配 30-60% LLM 呼叫費用
Redis Tool 結果(SAP、DB 查詢) 60-70% 外部 API 費用
CDN 不含個人化的 API 回應 10-20% 伺服器計算費用
──────────────────────────────────────────────────────────────
組合命中率(保守估計):50-65%
成本影響:LLM 費用 × (1 - 0.55) = 45% 的費用
快取的邊界條件(什麼不應該快取):
❌ 不應快取的查詢類型:
├── 含有「現在」「今天」「最新」等時效性詞彙
├── 個人化查詢(「我的帳戶餘額」)
└── 工具呼叫結果(高時效性,如庫存、匯率)
✅ 應快取的查詢類型:
├── 知識類查詢(「什麼是 RAG?」「合約中的違約金條款一般如何計算?」)
├── 政策和規則查詢(不常變動的)
└── 固定格式的報告生成(相同模板,不同時期資料不同)
六、三層速率限制
為什麼要分三層(不只是全域限制):
只有 Global 限制的問題:
└── 一個失控的客戶(10,000 RPM)耗盡全域配額
→ 其他所有客戶全部看到 429,但他們沒有做錯任何事
三層設計的隔離邏輯:
┌─────────────────────────────────────────────────────────────┐
│ Global Rate Limit(500,000 RPM) │
│ 保護後端基礎設施和 LLM API 配額 │
│ 超限:HTTP 503(系統過載,稍後再試) │
└──────────────────────┬──────────────────────────────────────┘
│ 通過後進入
▼
┌─────────────────────────────────────────────────────────────┐
│ Tenant Rate Limit(每個企業客戶,依方案配額) │
│ Enterprise:50,000 RPM | Standard:10,000 RPM | Free:500 RPM│
│ 超限:HTTP 429 + Retry-After header(告知何時重試) │
│ 效果:一個客戶失控不影響其他客戶 │
└──────────────────────┬──────────────────────────────────────┘
│ 通過後進入
▼
┌─────────────────────────────────────────────────────────────┐
│ User Rate Limit(每個終端用戶) │
│ 請求次數:100 RPM │
│ Token 配額:10,000 Tokens/天(與計費掛鉤) │
│ 超限:HTTP 429 + 用戶友好訊息(「今日配額已用完,明日重置」) │
└─────────────────────────────────────────────────────────────┘
Rate Limiting 算法選擇(Token Bucket):
Token Bucket 的運作:
├── 每個 Token 單位時間補充(1 Token/10ms = 100 Token/s)
├── 桶的容量 = 允許的突發量(burst = 200 Token)
├── 請求消耗 Token(每個請求 1 Token)
└── 桶空了 → 限流(429)
允許突發的設計意義:
└── 用戶在 1 秒內發送 50 個請求是合法的(burst)
→ Token Bucket 吸收突發,不直接拒絕
長期平均 > 限制才真正限流
Rate Limit State 的儲存:
└── Redis(滑動視窗計數器):
INCR rate_limit:{tenant_id}:{minute_bucket}
EXPIRE rate_limit:{tenant_id}:{minute_bucket} 120
七、資料庫擴展設計
三個 DB 擴展問題的優先順序:
問題 1(Phase 2 必解):讀寫混合
問題 2(Phase 2 必解):連接數耗盡
問題 3(Phase 3 需解):單一 DB 寫入瓶頸(通常是最後遇到的問題)
讀寫分離架構(Read Replica):
AI 系統的讀寫比典型值:
├── 讀取(查詢歷史對話、查詢用戶設定、讀取 Tool 結果):85%
└── 寫入(新增對話記錄、更新 Task Status):15%
設計:
Agent Service
│
├──→ PostgreSQL Primary(只處理寫入,15% 流量)
│
├──→ Read Replica A(查詢對話歷史,讀取 30%)
└──→ Read Replica B(查詢用戶設定、稽核,讀取 55%)
複製延遲(Replication Lag)的影響:
└── Primary 寫入後,Read Replica 通常 10-100ms 後才同步
需要考慮:
├── 用戶剛更新設定,立刻查詢 → 可能還是舊值
└── 解法:「寫後讀」(Read-your-writes)的請求強制打 Primary
Connection Pool 設計(PgBouncer):
問題:
100 個 Agent 實例 × 50 個 DB 連接 = 5,000 個連接
PostgreSQL 預設 max_connections = 100 → 直接崩潰
PgBouncer 解法:
100 個 Agent 實例
↓(每個實例 50 個連接 = 5,000 條連接)
PgBouncer(Connection Pooler)
↓(複用 50 個真實 DB 連接)
PostgreSQL(只有 50 個連接,在 max_connections 以內)
PgBouncer 模式選擇:
├── Session Mode:一個客戶端連接 = 一個 DB 連接(無複用)
├── Transaction Mode:同一 DB 連接在事務間複用 ← AI 系統推薦
└── Statement Mode:最激進複用(不支援事務)
資料分層儲存架構:
資料類型 儲存選擇 TTL/Retention
──────────────────────────────────────────────────────────────
Session(熱) Redis Cluster 24h(自動清理)
Conversation(溫) PostgreSQL + Replica 用戶主動刪除前保留
Vector Index Vector DB 永久(索引版本管理)
Task Result(冷) PostgreSQL 30 天後歸檔
Audit Log(冷) Object Storage 7-10 年(合規)
──────────────────────────────────────────────────────────────
設計原則:
├── 熱資料(高頻讀寫)→ Redis:延遲 < 5ms,無需 Schema
├── 溫資料(需查詢)→ PostgreSQL:支援 SQL 查詢和事務
└── 冷資料(稽核/歸檔)→ Object Storage:成本 $0.02/GB,不佔 DB 容量
八、Auto-scaling 設計與 Cold Start 消除
AI 系統 Auto-scaling 的特殊挑戰:
傳統服務:CPU 高 → 擴展(CPU 和工作量正相關)
AI 服務:等 LLM 回應時,Agent 的 CPU 幾乎是 0%
但此時連接槽可能已滿,系統實際在瓶頸中
結論:用 CPU 擴展 AI 服務是錯誤的指標。
Auto-scaling 指標選擇:
指標 反映的問題 適合場景
──────────────────────────────────────────────────────────────
CPU 計算密集型瓶頸 ❌ 不適合 AI 服務
──────────────────────────────────────────────────────────────
Memory 記憶體洩漏 作為輔助指標
──────────────────────────────────────────────────────────────
Concurrency 每個實例的並發請求數 ✅ AI 系統首選
(請求並發數) 直接反映連接槽壓力 超過 80% concurrency → 擴展
──────────────────────────────────────────────────────────────
Queue Depth 非同步佇列的積壓量 ✅ Worker Pool 的擴展指標
──────────────────────────────────────────────────────────────
P95 Latency 用戶體驗降級信號 ✅ 搭配 Concurrency 使用
建議配置(Phase 2-3):
Agent Service(同步路徑):
├── min-instances: 2(保持暖機,消除 Cold Start)
├── max-instances: 100
├── concurrency: 10(每個實例最多 10 個 LLM 並發)
├── 擴展觸發:concurrency 使用率 > 70% 持續 60s
└── 縮減延遲:scale-down-delay = 300s(防止頻繁啟停)
Worker Pool(非同步路徑):
├── min-instances: 0(非高峰期省成本)
├── max-instances: 200
└── 擴展觸發:Queue Depth > 50 持續 30s
Cold Start 問題與解法:
Cold Start 發生場景:
Scale-Out 新增實例 → 實例初始化(載入模型、建立連接)→ 8-15s 延遲
解法比較:
解法 消除效果 月成本 適合場景
──────────────────────────────────────────────────────────────
min-instances=2 100% $50-150/月 有 SLA 要求(P95 < 5s)
──────────────────────────────────────────────────────────────
Warmup Ping 90% $5/月 成本敏感,偶爾慢可接受
每 10 分鐘一次
──────────────────────────────────────────────────────────────
Predictive Scale 80% 中等 有規律的流量模式
(提前預置)
──────────────────────────────────────────────────────────────
min-instances 成本分析:
├── 2 個實例 × 0.5 vCPU × $0.00002/vCPU-s × 86400s × 30 天 ≈ $50/月
└── 對比 Cold Start 的業務損失:
每次 Cold Start 影響 N 個用戶,用戶流失率 × LTV > $50/月
→ min-instances = 2 是合理的投資
九、為什麼選 X 不選 Y:關鍵技術決策
決策 1:Session 儲存 → Redis vs Memcached vs PostgreSQL
┌──────────────┬───────────────────────────┬─────────────────────────┐
│ │ Redis(✅ 選擇) │ Memcached / PostgreSQL │
├──────────────┼───────────────────────────┼─────────────────────────┤
│ 延遲 │ < 1ms(in-memory) │ Memcached 相似; │
│ │ │ PostgreSQL 10-50ms │
├──────────────┼───────────────────────────┼─────────────────────────┤
│ TTL │ 原生支援(EXPIRE 命令) │ Memcached:有; │
│ │ │ PostgreSQL:需自己管理 │
├──────────────┼───────────────────────────┼─────────────────────────┤
│ 資料結構 │ List, Hash, Set 等豐富結構│ Memcached:只有 KV; │
│ │ │ PostgreSQL:完整 SQL │
├──────────────┼───────────────────────────┼─────────────────────────┤
│ 持久化 │ 可選(RDB/AOF) │ Memcached:無; │
│ │ │ PostgreSQL:完整 │
├──────────────┼───────────────────────────┼─────────────────────────┤
│ Cluster │ Redis Cluster 原生支援 │ Memcached 較複雜 │
└──────────────┴───────────────────────────┴─────────────────────────┘
結論:Redis 在低延遲、TTL、豐富資料結構三者兼顧,是 Session 儲存的最佳選擇。
決策 2:快取策略 → Semantic Cache vs Exact-match Cache
Exact-match Cache:「今天天氣如何」和「今天的天氣是什麼」→ 兩個 Miss
Semantic Cache:cosine_similarity(v1, v2) = 0.97 → Hit
適合 Exact-match 的場景:
└── Query 完全固定(系統生成的查詢,不是用戶輸入)
適合 Semantic Cache 的場景:
└── 用戶自然語言輸入(措辭多樣,語義相似)→ AI 系統的主要場景
決策 3:非同步通知 → WebSocket Push vs Polling
┌──────────────┬───────────────────────┬─────────────────────────────┐
│ │ WebSocket Push │ Polling │
├──────────────┼───────────────────────┼─────────────────────────────┤
│ 即時性 │ 毫秒級(任務完成即推)│ 最多 5s 延遲(每 5s 查詢) │
├──────────────┼───────────────────────┼─────────────────────────────┤
│ 伺服器負載 │ 維持長連接 │ 無長連接 │
├──────────────┼───────────────────────┼─────────────────────────────┤
│ 實作複雜度 │ 高(需要 WS 伺服器) │ 低(HTTP Endpoint) │
├──────────────┼───────────────────────┼─────────────────────────────┤
│ 適合場景 │ Web App(長期在線) │ Mobile / 簡單場景 │
└──────────────┴───────────────────────┴─────────────────────────────┘
決策 4:Rate Limiting 算法 → Token Bucket vs Fixed Window vs Sliding Window
Fixed Window 的問題:
└── 23:59:59 發送 100 個請求(本分鐘上限)
00:00:00 再發送 100 個請求(新分鐘)
= 1 秒內 200 個請求,繞過了 100 RPM 的限制
Token Bucket 的優勢:
├── 允許合理的突發(burst capacity)
├── 長期強制平均速率
└── 不存在跨窗口邊界的漏洞
→ AI 服務推薦 Token Bucket
決策 5:DB 讀取擴展 → Read Replica vs Sharding
Sharding 的代價:
├── 跨 Shard 查詢複雜(需要 Scatter-Gather)
├── 資料不均衡(某些 Shard 過熱)
└── Schema 變更困難
什麼時候才需要 Sharding:
└── 寫入成為瓶頸(Primary DB 的寫入 QPS 達到上限)
一般 PostgreSQL Primary 可以支撐 10,000+ WPS
→ AI 系統在 1M 用戶以下,通常 Read Replica 足夠
→ Sharding 是更後期的問題
決策 6:Auto-scaling 指標 → Concurrency vs CPU
AI 服務等待 LLM 時的資源狀態:
├── CPU:~5%(等待,不計算)
├── Memory:正常
└── 並發連接數:已滿(每個實例的連接槽被佔用)
用 CPU 擴展 → CPU 低,不擴展 → 但連接槽滿了 → 新請求排隊 → 延遲爆炸
用 Concurrency 擴展 → 連接槽 > 70% → 立刻擴展 → 延遲穩定
十、系統效應:擴展設計的量化對比
維度 Phase 1(10K) Phase 2(100K) Phase 3(1M)
──────────────────────────────────────────────────────────────────────
LLM 成本/MAU $4.00 $2.50 $1.50
(快取命中率) 0%快取 30%語意快取 55%組合快取
──────────────────────────────────────────────────────────────────────
P95 延遲 3s 3s 3s
(Cold Start) 偶爾 15s Cold < 4s(min=2) < 4s(min=2)
──────────────────────────────────────────────────────────────────────
Scale-Out 效果 ❌ Session 斷掉 ✅ 無狀態,有效 ✅ 全面有效
──────────────────────────────────────────────────────────────────────
惡意用戶影響 拖垮整個系統 影響單一 Tenant Rate Limit 隔離
──────────────────────────────────────────────────────────────────────
長任務體驗 同步等待/超時 同步等待 非同步 202,不超時
──────────────────────────────────────────────────────────────────────
月架構成本 $500 $1,500 $4,000-6,000
月 LLM 成本(1M MAU)不適用 不適用 $1,500,000 × 45% 省去
≈ $825,000 節省
──────────────────────────────────────────────────────────────────────
Phase 3 架構投入的 ROI 計算:
額外架構成本:~$4,000/月
LLM 成本節省(55% 快取):~$825,000/月(1M MAU 規模)
ROI:200x+
即使在 100K MAU 規模:
LLM 節省:$82,500 × 30% = $24,750/月
架構成本:$1,500/月
ROI:16x
十一、面試答題要點
「這道題的核心不是『加幾台機器』,而是找出哪些架構假設在 10K 時成立、在 1M 時失效——然後系統性地替換它們。
我會用三個演進階段來回答:Phase 1 是 POC,接受有狀態設計;Phase 2 是生產試點,核心改動是無狀態化(Session 移到 Redis)+ 讀寫分離;Phase 3 是規模化,加入語意快取、非同步佇列和三層速率限制。
四個關鍵技術決策:第一,用 Redis 做 Session 儲存而不是 DB,因為 < 1ms 延遲 + 原生 TTL 是 Session 場景的必要條件。第二,語意快取而不是 Exact-match,因為用戶自然語言查詢的字面多樣但語義重複,Exact-match 命中率 < 5%,語意快取可達 40-60%。第三,Token Bucket Rate Limiting 而不是 Fixed Window,因為 Fixed Window 有跨邊界的 2x 漏洞。第四,用 Concurrency 而不是 CPU 作為 Auto-scaling 指標,因為 AI 服務等 LLM 時 CPU 幾乎是 0,但連接槽可能已滿。
成本視角:在 1M MAU 規模,Phase 3 架構每月額外架構成本約 $4,000,但 55% 的快取命中率讓 LLM 成本降低超過 $800,000/月。這個架構投入的 ROI 超過 200x。」
系列導覽:
← (三十八)從 POC 到 Production:生產化清單
→ (四十)AI 系統的 PII 保護:資料脫敏與合規稽核
