為什麼會有分散式架構?一篇文章帶你看懂演進過程

Published August 6, 2025 by 徐培鈞
架構

你有沒有想過,為什麼 Netflix 可以同時讓全世界數百萬人看影片不會當機?為什麼淘寶雙 11 可以處理這麼大的購物流量?答案就是「分散式架構」。

別被這個名詞嚇到,其實分散式架構就像是把原本一個人要做的所有工作,分給一群專業的人來做,每個人負責自己最擅長的部分。這篇文章會用最簡單的方式,帶你看懂這個架構是怎麼一步一步演變出來的。

一開始很簡單:所有東西放一台電腦

graph TD
    A[用戶請求] --> B[單一主機]
    B --> C[業務邏輯]
    B --> D[資料庫]
    B --> E[檔案儲存]
    B --> F[回應給用戶]

剛開始的美好時光

想像你開了一個小網站,可能是賣手作商品或分享生活照片。一開始用戶不多,一台普通電腦就能搞定所有事情:

  • 處理用戶的請求
  • 儲存資料
  • 放置圖片和影片

就像一個人經營小店面,老闆既當櫃檯、又當會計、還要管倉庫。

問題來了:突然爆紅怎麼辦?

有一天你的網站突然火了!可能被某個網紅分享,或者上了新聞,瞬間湧入大量用戶。這時候會發生什麼?

你的電腦開始:

  • 回應變慢
  • 有時候當機
  • 用戶連不上網站

就像小店面突然來了一大群客人,老闆一個人忙不過來,客人只能排隊等待。

第一個解決方案:買更強的電腦

垂直擴展的想法

最直觀的想法就是:既然電腦不夠力,那就換一台更強的!

  • CPU 升級
  • 記憶體加大
  • 硬碟容量提升

這就像把小店面換成大店面,老闆一個人但空間更大、設備更好。

為什麼這個方法不行?

問題是,如果用戶持續增加,你就要一直升級設備:

  • 普通電腦 → 高階電腦 → 工作站 → 超級電腦

最後可能需要一台「超級電腦」才能應付,但是:

  • 超級電腦超級貴:一般公司根本買不起
  • 有極限:再強的單台電腦也有上限
  • 風險高:一台電腦壞了,整個服務就停了

就像再大的店面,一個老闆還是有極限,而且店面出問題整個生意就停擺。

更聰明的做法:多台電腦一起工作

graph TD
    A[用戶請求] --> B[Load Balancer]
    B --> C[主機 1]
    B --> D[主機 2]  
    B --> E[主機 3]
    C --> F[處理請求]
    D --> F
    E --> F

水平擴展的概念

架構師們想到一個更聰明的方法:

「與其買一台超強電腦,不如用多台普通電腦一起工作!」

這時候架構變成:

  • 多台普通主機並排工作
  • 前面放一個「Load Balancer」(負載平衡器)來分配工作
  • 用戶請求進來時,Load Balancer 決定讓哪台主機處理

Load Balancer 是什麼?

Load Balancer 就像餐廳的帶位人員:

  • 客人(用戶請求)進來時
  • 帶位員看哪個服務生(主機)比較不忙
  • 把客人安排到合適的桌子(主機)

這樣做的好處

  • 便宜:普通電腦比超級電腦便宜太多
  • 彈性:流量大就多加機器,流量小就減少機器
  • 穩定:一台壞了,其他台還能繼續工作

資料要放哪裡?資料庫服務登場

graph TD
    A[用戶請求] --> B[Load Balancer]
    B --> C[主機 1]
    B --> D[主機 2]
    B --> E[主機 3]
    C --> F[共用資料庫]
    D --> F
    E --> F
    F --> G[統一資料來源]

新問題出現

現在有多台主機在工作,但問題來了:資料要放在哪裡?

如果每台主機都有自己的資料,就會出現:

  • 用戶在 A 主機註冊帳號,但 B 主機找不到這個帳號
  • 同一筆訂單在不同主機上看到不同結果

解決方案:獨立的資料庫

把資料庫獨立出來,所有主機都連到同一個資料庫:

  • 主機們負責處理請求
  • 資料庫專門負責儲存和提供資料
  • 保證所有主機看到的資料都一致

就像多個店員都查看同一本客戶名冊,不會出現資訊不一致的問題。

資料庫也會累:讀寫分離

graph TD
    A[用戶請求] --> B[Load Balancer]
    B --> C[主機群組]
    C --> D{讀取 or 寫入?}
    D -->|寫入| E[Master DB<br/>主資料庫]
    D -->|讀取| F[Read Replica 1<br/>從資料庫]
    D -->|讀取| G[Read Replica 2<br/>從資料庫]
    D -->|讀取| H[Read Replica 3<br/>從資料庫]
    E -.->|資料複製| F
    E -.->|資料複製| G  
    E -.->|資料複製| H

資料庫的瓶頸

隨著用戶越來越多,連資料庫也開始忙不過來。仔細觀察後發現:

  • 大部分時候用戶都在「看」資料(讀取)
  • 只有少部分時候在「改」資料(寫入)

比如在社群網站上,大家多數時間在瀏覽貼文,只有少數時候發新貼文。

讀寫分離的概念

既然讀取比寫入多很多,那就分開處理:

主資料庫(Master)

  • 專門處理寫入操作(新增、修改、刪除)
  • 只有一台,確保資料一致性

從資料庫(Read Replica)

  • 專門處理讀取操作
  • 可以有很多台
  • 資料從主資料庫複製過來

就像圖書館裡,只有一個地方可以辦借書手續(寫入),但可以有很多個閱覽室讓人看書(讀取)。

特殊情況:NoSQL 的考量

如果你的應用寫入操作特別頻繁(比如即時聊天、遊戲積分等),傳統關聯式資料庫可能還是不夠用。

這時候可能會考慮 NoSQL 資料庫:

  • 犧牲一些資料一致性
  • 換取更高的讀寫效能
  • 適合特定的使用場景

檔案太多太大:影音圖檔服務

graph TD
    A[用戶:我要看商品圖片] --> B[Load Balancer]
    B --> C[主機群組]
    C --> D[資料庫:查詢圖片路徑]
    D --> C
    C --> E[回應:圖片網址<br/>https:\//files.example.com/product123.jpg]
    E --> F[用戶瀏覽器自動請求圖片網址]
    F --> G[影音圖檔服務]
    G --> H[從儲存系統取得圖片]
    H --> I[圖片傳送給用戶]
    
    style A fill:#e3f2fd
    style E fill:#f3e5f5
    style I fill:#e8f5e8

新的負擔

隨著業務發展,網站上的圖片、影片越來越多。原本的處理方式是:

  • 檔案存放:影音檔案直接放在主機的硬碟裡
  • 路徑記錄:資料庫只存檔案的路徑位置(如:/images/photo123.jpg
  • 檔案傳輸:用戶要看圖片時,主機負責把檔案傳送給用戶

但這樣會產生問題:

  • 檔案很大,佔用主機大量儲存空間
  • 用戶下載檔案時,消耗主機的網路頻寬和處理能力
  • 主機要同時處理業務邏輯又要傳送大檔案,效能變差

獨立的檔案服務

解決方法是把檔案處理獨立出來:

分工合作的概念

  • 主機:專心處理業務邏輯,查詢資料庫取得檔案路徑
  • 影音圖檔服務:專門負責儲存檔案和處理下載請求
  • 資料庫:依然只存檔案路徑,但路徑指向影音圖檔服務

實際運作流程

  1. 用戶要看某張照片
  2. 主機處理請求,從資料庫查到照片路徑
  3. 主機告訴用戶:「照片在影音圖檔服務的這個位置」
  4. 用戶直接向影音圖檔服務下載照片
  5. 主機不用負擔檔案傳輸,可以處理更多業務請求

就像餐廳把飲料供應外包給專業飲料公司,服務生不用自己調飲料,可以專心服務客人。

減輕資料庫負擔:快取服務

graph TD
    A[用戶請求] --> B[Load Balancer]
    B --> C[主機群組]
    C --> D{資料在快取中?}
    D -->|是| E[快取服務<br/>Cache]
    D -->|否| F[資料庫服務]
    E --> G[快速回應]
    F --> H[查詢結果]
    H --> I[存入快取]
    I --> G

重複查詢的浪費

仔細觀察用戶行為會發現:

  • 熱門商品被反覆查詢
  • 用戶資料經常被重複讀取
  • 每次都去資料庫查詢很浪費

快取服務(Cache)的概念

快取就像是記憶功能:

  • 第一次查詢時,結果存一份在快取裡
  • 下次有人查詢同樣內容,直接從快取回答
  • 不用每次都麻煩資料庫

實際運作

  1. 用戶查詢「iPhone 最新價格」
  2. 第一次:去資料庫查,結果存在快取裡
  3. 接下來 100 個用戶查同樣問題:直接從快取回答

就像店員把常被問的問題答案記在小紙條上,不用每次都翻厚厚的手冊。

全球服務的挑戰:CDN 登場

graph TD
    A[台灣用戶要看影片] --> B[台灣 CDN]
    C[美國用戶要看影片] --> D[美國 CDN]
    E[歐洲用戶要看影片] --> F[歐洲 CDN]
    
    B --> B1{影片在本地快取?}
    D --> D1{影片在本地快取?}
    F --> F1{影片在本地快取?}
    
    B1 -->|有| B2[直接提供影片<br/>⚡ 超快速]
    B1 -->|沒有| G[台灣總部<br/>原始影音服務]
    G --> B3[下載並快取影片]
    B3 --> B2
    
    D1 -->|有| D2[直接提供影片<br/>⚡ 超快速]
    D1 -->|沒有| G
    G --> D3[下載並快取影片]
    D3 --> D2
    
    F1 -->|有| F2[直接提供影片<br/>⚡ 超快速]
    F1 -->|沒有| G
    G --> F3[下載並快取影片]
    F3 --> F2
    
    style A fill:#e3f2fd
    style C fill:#e3f2fd
    style E fill:#e3f2fd
    style G fill:#ffcdd2
    style B2 fill:#c8e6c9
    style D2 fill:#c8e6c9
    style F2 fill:#c8e6c9

距離是個問題

如果你的服務面向全球用戶(像 Netflix),會遇到一個物理問題:

  • 服務器在台灣
  • 俄羅斯用戶要看影片
  • 資料要傳輸半個地球,速度會很慢

CDN 是什麼?

CDN(Content Delivery Network,內容分發網絡)解決這個問題:

運作方式

  1. 把熱門內容複製到全球各地的服務器
  2. 用戶請求時,從最近的服務器提供內容
  3. 速度更快,用戶體驗更好

實際例子

  • Netflix 在台灣、日本、美國等地都有服務器
  • 台灣用戶看影片時,直接從台灣的服務器下載
  • 不用繞到美國總部,速度快很多

就像連鎖咖啡店在各地都開分店,客人不用跑到總店就能買到咖啡。

不急的工作:非同步服務

graph TD
    A[用戶上傳影片] --> B[Load Balancer]
    B --> C[主機群組]
    C --> D[快速回應:已收到]
    C --> E[放入訊息佇列<br/>Message Queue]
    E --> F[非同步服務<br/>專門處理重任務]
    F --> G[影片壓縮轉檔]
    F --> H[產生縮圖]
    F --> I[病毒掃描]
    G --> J[處理完成通知]
    H --> J
    I --> J
    J --> K[通知用戶:影片已上線]

重任務會拖垮系統

想像如果 YouTube 的影片上傳是這樣處理的:

  1. 用戶上傳一部 1GB 的影片
  2. 主機開始處理:壓縮、轉檔、產生縮圖、病毒掃描…
  3. 在處理過程中(可能需要 10-30 分鐘),這台主機就卡住了
  4. 其他用戶的請求都要排隊等待

結果就是:

  • 一個影片上傳就能讓整台服務器停擺
  • 其他用戶連簡單的登入、瀏覽都做不了
  • 系統效能極差,用戶體驗糟糕

非同步處理的救援

聰明的做法是把這些「重任務」分離出來:

立即回應,背景處理

  1. 用戶上傳影片:主機快速接收檔案,立即回應「上傳成功,正在處理中」
  2. 釋放主機:主機馬上可以處理其他用戶的請求
  3. 背景工作:專門的非同步服務慢慢處理影片轉檔、壓縮等工作
  4. 完成通知:處理完成後通知用戶「影片已上線」

實際運作流程

  • 主機只負責「接收」和「快速回應」(幾秒鐘就完成)
  • 重任務交給後台服務慢慢處理(幾分鐘到幾小時)
  • 主機永遠保持高效能,可以同時服務成千上萬用戶

訊息佇列:工作排程系統

graph LR
    A[影片A上傳] --> D[訊息佇列]
    B[影片B上傳] --> D
    C[影片C上傳] --> D
    D --> E[工作者1:處理影片A]
    D --> F[工作者2:處理影片B]  
    D --> G[工作者3:處理影片C]

訊息佇列的作用

  • 排隊機制:把所有待處理的工作依序排列
  • 流量控制:避免同時處理太多重任務導致系統過載
  • 可靠性:即使某個工作失敗,也不會影響其他工作

實際例子

  • 同時有 100 個人上傳影片
  • 訊息佇列依序安排處理順序
  • 10 個工作程序同時處理不同影片
  • 每個工作程序處理完一部影片,就接下一部
  • 主服務器完全不受影響,依然可以處理其他請求

其他適用場景

除了影片處理,還有很多情況適合用非同步服務:

  • 大量郵件發送:電商網站發促銷郵件給百萬用戶
  • 資料匯出:生成大型報表或帳單
  • 圖片處理:批量調整圖片大小、加浮水印
  • 資料分析:計算用戶行為統計、推薦算法

就像快遞公司不會讓寄件員等包裹送達才能收下一個包裹,而是先收件給收據,再由配送系統慢慢處理。

系統監控:眼觀四面,耳聽八方

graph TD
    A[Load Balancer] -.->|監控數據| H[監控中心]
    B[主機群組] -.->|監控數據| H
    C[資料庫服務] -.->|監控數據| H
    D[快取服務] -.->|監控數據| H
    E[影音圖檔服務] -.->|監控數據| H
    F[非同步服務] -.->|監控數據| H
    G[CDN] -.->|監控數據| H
    H --> I[即時警報]
    H --> J[效能儀表板]

    A -.->|日誌| K[集中日誌系統]
    B -.->|日誌| K
    C -.->|日誌| K
    D -.->|日誌| K
    E -.->|日誌| K
    F -.->|日誌| K
    K --> L[問題追蹤分析]

分散式系統的複雜性

現在我們的系統分散在很多地方:

  • Load Balancer
  • 多台主機
  • 資料庫群組
  • 檔案服務
  • 快取系統
  • CDN
  • 非同步服務

問題是:這麼多組件,怎麼知道哪裡出問題?

監控中心的重要性

需要一個「中央監控系統」來監視所有組件:

收集指標(Metrics)

  • CPU 使用率
  • 記憶體用量
  • 磁碟空間
  • 網路流量
  • 回應時間

即時警報

  • 任何地方出現異常立即通知
  • 預防小問題變成大災難

日誌管理(Log Service)

除了監控,還需要「集中化日誌管理」:

為什麼需要?

  • 一個用戶請求可能經過多個服務
  • 出錯時需要追蹤整個過程
  • 分散在各地的日誌很難分析

實際運作

  1. 所有服務把日誌送到中央日誌系統
  2. 用戶回報問題時,可以追蹤整個請求路徑
  3. 快速定位問題發生在哪個環節

就像醫院裡每個部門都要記錄病患的診療過程,最後整合成完整的病歷。

分散式架構總結

graph TD
    A[用戶請求] --> B[Load Balancer]
    B --> C[運算服務叢集]

    C --> D[快取服務]
    C --> E[資料庫服務]
    C --> F[影音圖檔服務]
    C --> G[非同步服務]

    E --> E1[Master DB]
    E --> E2[Read Replica 1]
    E --> E3[Read Replica 2]

    F --> H[CDN 全球分布]
    G --> I[訊息佇列]

    J[監控中心] -.->|監控| B
    J -.->|監控| C
    J -.->|監控| D
    J -.->|監控| E
    J -.->|監控| F
    J -.->|監控| G

    K[日誌中心] -.->|收集日誌| B
    K -.->|收集日誌| C
    K -.->|收集日誌| D
    K -.->|收集日誌| E
    K -.->|收集日誌| F
    K -.->|收集日誌| G

    style A fill:#e1f5fe
    style J fill:#fff3e0
    style K fill:#f3e5f5

核心目的:解決高併發流量

分散式架構出現的根本原因是:

  • 單台電腦有極限
  • 超級電腦太昂貴
  • 必須找到經濟實用的擴展方式

核心方法:專業分工 + 水平擴展

專業分工

  • 運算服務:處理業務邏輯
  • 資料庫服務:管理資料
  • 檔案服務:處理影音圖檔
  • 快取服務:加速常用查詢
  • 非同步服務:處理背景工作

水平擴展

  • 需要更多處理能力時,增加更多機器
  • 流量減少時,減少機器數量
  • 彈性調整,成本控制

帶來的挑戰與解決方案

挑戰 1:資料同步

  • 問題:多個地方存放資料,如何保持一致?
  • 解決:設計合適的資料同步機制

挑戰 2:系統監控

  • 問題:組件分散各地,如何知道系統狀態?
  • 解決:集中化監控和警報系統

挑戰 3:問題追蹤

  • 問題:一個請求經過多個服務,出錯時如何定位?
  • 解決:集中化日誌管理系統

結語

分散式架構聽起來複雜,但其實就是把一個人做的工作分給一群專業人士來做。每個組件都有自己的專長,互相配合完成用戶的需求。

隨著業務的成長,系統會自然而然地朝這個方向演進。理解這個演進過程,不僅能幫助你更好地設計系統,也能讓你在面對技術決策時有更清晰的判斷。

記住,分散式架構不是一開始就要做到最複雜,而是根據實際需求逐步演進。從簡單開始,遇到瓶頸時再優化,這才是最實用的方法。