核心元件(Core Components)

是什麼?

分散式系統的四大核心元件,各自解決不同的效能和可靠性問題。理解它們的職責和適用場景,是 System Design 的基本功。

ℹ️記住這個口訣

LCMQLoad Balancer 分流量、Cache 省查詢、Message Queue 解耦合、CDN 近用戶。每個元件對應一種瓶頸。

核心觀念

Load Balancer

Cache

Message Queue

CDN

常見誤區

⚠️Cache 不是銀彈

Cache Invalidation 是電腦科學最難的問題之一。快取資料和資料庫不一致會造成嚴重 bug。永遠設定 TTL,並在資料更新時主動清除快取。別把所有東西都快取 — 只快取「讀多寫少」的熱點資料。

設計流程

1

分析流量模式

讀多寫少?需要即時?有流量尖峰?

2

加入 Load Balancer

多台伺服器前面放 LB,選擇 L4 或 L7

3

加入 Cache 層

熱點資料放 Redis,減少資料庫壓力

4

加入 Message Queue

非同步任務放入 Queue,削峰填谷

5

加入 CDN

靜態資源上 CDN,減少 Origin 負載和延遲

程式碼範例

Cache Aside Pattern(C#)

csharp
public class ProductService
{
    private readonly IDistributedCache _cache;
    private readonly ProductRepository _repo;
 
    public async Task<Product?> GetProduct(int id)
    {
        // 1. 先查快取
        var cached = await _cache.GetAsync<Product>($"product:{id}");
        if (cached != null) return cached;
 
        // 2. 快取沒有,查資料庫
        var product = await _repo.FindByIdAsync(id);
        if (product == null) return null;
 
        // 3. 寫入快取,設定 TTL
        await _cache.SetAsync($"product:{id}", product, TimeSpan.FromMinutes(10));
        return product;
    }
 
    public async Task UpdateProduct(Product product)
    {
        await _repo.UpdateAsync(product);
        // 4. 更新時清除快取,下次讀取會重新載入
        await _cache.RemoveAsync($"product:{product.Id}");
    }
}

Message Queue 消費者(TypeScript)

typescript
import amqplib from "amqplib";
 
// Producer:發送訂單事件
async function publishOrder(order: Order) {
  const conn = await amqplib.connect("amqp://localhost");
  const channel = await conn.createChannel();
  await channel.assertQueue("orders");
  channel.sendToQueue("orders", Buffer.from(JSON.stringify(order)));
}
 
// Consumer:非同步處理訂單
async function consumeOrders() {
  const conn = await amqplib.connect("amqp://localhost");
  const channel = await conn.createChannel();
  await channel.assertQueue("orders");
  channel.consume("orders", async (msg) => {
    if (!msg) return;
    const order: Order = JSON.parse(msg.content.toString());
    await processOrder(order); // 寄信、扣庫存、記帳...
    channel.ack(msg);
  });
}

Load Balancer 設定(Nginx)

nginx
upstream backend {
    least_conn;  # 最少連線數演算法
    server 10.0.0.1:8080 weight=3;
    server 10.0.0.2:8080 weight=1;
    server 10.0.0.3:8080 backup;  # 備援伺服器
}
 
server {
    listen 80;
    location / {
        proxy_pass http://backend;
    }
}

架構圖

Users
static assets
CDN (CloudFront)
Load Balancer (Nginx)
distribute
App Server 1
read/write
App Server 2
read/write
Cache (Redis)
Message Queue (RabbitMQ)
consume
Database
Worker Service

架構圖解讀:用戶請求分兩條路 — 靜態資源(圖片、JS、CSS)由 CDN 就近回應,API 請求透過 Load Balancer 分配到 App Server。App Server 先查 Redis Cache,Cache Miss 才查 Database。需要非同步處理的任務(如寄信)透過 RabbitMQ 發給 Worker Service,不阻塞 API 回應。

實戰補充

💡選型決策樹

  • Cache:需要 Pub/Sub 或複雜資料結構 → Redis;純 key-value 高速快取 → Memcached
  • Message Queue:需要高吞吐量日誌流 → Kafka;需要靈活路由 → RabbitMQ;想省事 → AWS SQS
  • CDN:已在 AWS 生態 → CloudFront;需要 WAF + CDN 一體化 → Cloudflare
  • Load Balancer:需要高度自定義 → Nginx/HAProxy;雲端環境 → AWS ALB/NLB

理解測驗

🤔 一個電商網站的商品頁面每秒被瀏覽 10,000 次,但商品資料每小時才更新一次。最適合加入哪個元件來優化?

🤔 用戶註冊後需要:寄歡迎信、建立預設設定、通知推薦系統。這些步驟不需要在註冊 API 回應前完成。最適合用哪個元件?

🤔 L4 和 L7 Load Balancer 的主要差異是什麼?

重點整理

💡一句話記住

LB 分流量、Cache 省查詢、MQ 解耦合、CDN 近用戶。 記憶口訣:「分省解近」— 每個元件解決一種瓶頸,缺哪個就加哪個。

| 元件 | 解決的問題 | 常見選型 | 使用時機 | |------|-----------|---------|---------| | Load Balancer | 流量集中在單一伺服器 | Nginx、ALB | 多台伺服器需要分流 | | Cache | 資料庫查詢太頻繁 | Redis、Memcached | 讀多寫少的熱點資料 | | Message Queue | 服務耦合、流量尖峰 | RabbitMQ、Kafka | 非同步處理、削峰填谷 | | CDN | 用戶離伺服器太遠 | CloudFront、Cloudflare | 靜態資源全球分發 |

你可能也想看

可擴展性基礎資料庫擴展

按 ← → 鍵切換課程