# 1.21 大型網站架構演化發展歷程

這篇是從[這裡](http://www.hollischuang.com/archives/728)看來的, 覺得很不錯所以就自己整理一次放上來:

大型網站系統的特點:

* 高並發, 大流量: 需要面對高並發使用者, 大流量訪問. Google日均PV: 35億(2011年)
* 高可用性: 7x24不間斷服務
* 海量資料: 需要儲存、管理海量資料, 需要使用大量server.
* 使用者分佈廣泛, 網路情況複雜: 許多大型網站都是為全球使用者提供服務的, 使用者分佈範圍廣, 各地的網路情況千差萬別.
* 安全環境惡劣: 由於Internet的開放性, 使得網站更容易受到攻擊, 大型網站基本上每天都會被打.
* 需求快速變更, 發布頻繁: 跟傳統軟體的發布頻率不同, Internet產品為了要快速適應市場, 滿足使用者需求, 其產品發布頻率極高. 一般大型網站的產品每週都有新版本發佈, 中小型的網站可能更頻繁, 一天發佈多次.
* 漸進式的發展: 幾乎所有的大型網站都是從一個小網站開始, 逐漸發展起來的. 好的架構是演進而來的, 不是一開始就設計出來的.

大型網站的技術挑戰有很多, 最常見的有以下幾點:

* **龐大的使用者數量**
* **高並發的存取(High Concurrency)**
* **海量的資料**

任何一個簡單的業務一但需要處理以Peta為數量級的資料與面對數以億計的使用者, 問題就會瞬間變得很棘手, 而大型網站架構就是想要解決這類問題.

## 初始階段的網站架構

大網站一開始都是從小型網站演化來的, 網站架構也一樣, 是從小型網站架構逐步演變而來. 一開始的小型網站沒什麼人會來, 也許一台server就可以撐住了, 這時候的架構可能像這樣:

![](/files/-M523rZI-bry9hTr4oqb)

應用程式、資料庫與文件等等, 所有的資源都在同一台server上.

## 應用程式與資料分離

隨著網站業務的發展, 一台server逐漸不能滿足需求了: 越來越多的使用者存取導致server的性能越來越差, 越來越多的資料導致儲存空間不足. 這時候就可以把應用程式跟資料做分離的動作. 分離之後整個網站使用三台server: 應用程式server、文件server以及資料庫server. 當然, 這三台server對硬體的需求也是不同的.

* **Application Server: 需要處理大量的業務邏輯, 因此需要更快更強大的CPU**
* **Document Server: 需要快速的磁碟檢索跟資料快取, 所以需要更快的磁碟與更大的記憶體**
* **Database Server: 需要儲存大量使用者上傳的文件, 因此需要更大的硬碟**

此時的網站架構可能像這樣:

![](/files/-M523rZKn3rZADP80KIU)

應用程式跟資料分離後, 不同特性的server承擔不同的角色, 網站的並發處理能力跟資料儲存空間得到了改善, 所以可以進一步支持網站的業務發展了. 然而, 使用者又繼續增加了, 網站又碰到了新的挑戰: 資料庫壓力太大導致存取latency增加, 進而影響了整個網站的效能, UX受到影響...

## 使用Cache改善網站性能

網站存取的特性跟現實世界的財富分配很相似, 遵循著二八法則: **80%的業務存取集中在20%的資料上**. 既然大部分的業務存取集中在一小部分的資料上, 那麼若把這一小部分的資料快取在記憶體當中, 就可以減少資料庫的存取壓力, 進而提高網站的資料存取速度並且改善資料庫的寫入性能了. 網站使用的快取可以分為兩種: 位於應用程式server上的local cache以及專門的分布式快取server上的遠端快取.

此時的網站架構可能像這樣:

![](/files/-M523rZM6l7GYOdtNBtv)

其中:

* **Local cache的存取速度更快一些, 但是受應用程式server的記憶體限制, 其快取資料量有限, 且會出現和應用程式搶記憶體的情況.**
* **遠端分布式快取可以使用叢集的方式, 部署帶有大容量記憶體的server作為專門的快取server, 可以在理論上做到不受記憶體容量限制的快取服務.**

使用快取後, 資料存取壓力得到有效的緩解, 但是單一應用程式server能夠處理的請求連接是有限的, 在網站存取的高峰期, 應用程式server會變成整個網站的瓶頸.

## 使用應用程式伺服器叢集改善網站的並發處理能力

使用叢集(Cluster)是網站解決高並發與海量資料的常用手段. 當一台server的處理能力或儲存空間不足的時候, 不要企圖去更換更強大的server, 對大型網站而言, 不管多強大的server基本上都無法滿足網站持續增長的業務需求. 這種情況下, 更適合的做法是增加一台server來分擔原有server的存取及儲存壓力. **對網站架構而言, 只要能通過增加一台server的方式改善負載壓力, 就可以如法泡製持續增加server以改善系統的性能, 從而實現系統的可伸縮性(scalability).** 應用程式server實作cluster是網站可伸縮架構設計中較為簡單成熟的一種, 大概會長這樣:

![](/files/-M523rZOqO_6xuNG5iY-)

通過load balancer, 可以將來自使用者瀏覽器的存取請求分派到應用程式server群中的任何一台server上, 如果有更多使用者, 就在cluster中加入更多的應用server, 使應用程式server的壓力不再成為整個網站的瓶頸.

## 資料庫的讀寫分離

網站在使用快取後, 使對大部分資料的讀取操作請求都可以不通過資料庫就能完成, 但是仍然有一部分的讀取(快取沒命中或是過期)和全部的寫操作都要存取資料庫, 在網站的使用者達到一定規模後, 資料庫因為負載壓力過高而成為網站的瓶頸. 目前大部分的主流資料庫都提供主從式的熱備援功能, 通過配置兩台資料庫(master-slave), 可以將一台資料庫server的資料同步更新到另一台server上. 網站利用資料庫的這一個功能, 實現資料庫的讀寫分離, 從而改善資料庫負載壓力, 這時候的架構大概長得像這樣:

![](/files/-M523rZQ42u_p7a5M_Dd)

應用程式server在寫資料的時候, 存取master, 而master通過master-slave replication機制將資料同步更新到slave, 這樣當應用程式server讀取資料的時候, 就可以通過slave獲得資料. 為了便於應用程式存取讀寫分離後的資料庫, 通常在應用程式server使用專門的資料存取模組, 使資料庫讀寫分離對應用程式透明.

## 引入反向代理與CDN加速網站的回應能力

隨著網站業務不斷發展, 使用者規模越來越大, 在某些複雜的網路環境中, 不同地區的使用者存取網站時, 速度差別也極大. 有研究顯示, 網站存取延遲跟使用者流失率呈現正相關, 網站存取越慢, 用戶越容易失去耐心而離開. 為了提供更好的UX, 留住用戶, 網站需要加速存取速度. 主要手段有導入CDN和反向代理, 此時架構大概如下:

![](/files/-M523rZSMpmeyfGN5Hv8)

CDN跟反向代理的基本原理都是快取.

* **CDN可以部署在ISP的機房, 使用者在請求網站服務的時候, 可以從距離自己最近的ISP機房獲得資料.**
* **反向代理則部署在網站的中心機房, 當使用者請求到達中心機房後, 首先存取的server是反向代理server, 若反向代理server中快取著用戶請求的資源, 就將其直接回傳給使用者.**

使用CDN和反向代理的目的都是希望可以盡快回傳資料給使用者, 一方面加快使用者的存取速度, 另一方面也減輕後端server的負載壓力.

## 使用分散式文件系統與分散式資料庫系統

任何強大的單一server都滿足不了大型網站持續增長的業務需求. 資料庫經過讀寫分離後, 從一台server拆分成兩台server, 但是隨著網站業務的發展依然不能滿足需求, 這時就需要使用分布式資料庫. 文件系統也是, 一樣需要使用分布式文件系統, 所以可能會變這樣:

![](/files/-M523rZUENKFpEkyiCC8)

**分布式資料庫是網站資料庫拆分的最後手段, 只有在單表資料規模非常龐大的時候才使用. 不到緊要關頭, 網站更常用的資料庫拆分手段是按照業務分資料庫, 將不同業務的資料部署至不同的物理server上.**

## 使用NoSQL和搜尋引擎

好了, 網站業務又變得更複雜了, 對資料儲存和檢索的需求也越來越複雜, 網站需要採用一些非關聯式資料庫技術如NoSQL和非資料庫查詢技術如搜尋引擎. 如此架構可能會變成這樣:

![](/files/-M523rZWjeiodIhhSh8Z)

NoSQL和搜尋引擎都是源自Internet的技術手段, 對可伸縮的分布式特性具有更好的支持. 應用程式server則通過一個統一資料存取模組來存取各種資料, 減輕應用程式管理眾多資料源的麻煩.

## 業務的拆分

大型網站為了應付日益複雜的業務場景, 通過使用divide and conquer的手段將整個網站業務分成不同的產品線. 以一個大型的購物交易網站來說, 會將首頁、商店、訂單、買家與賣家等拆分成不同的產品線, 分給不同的業務團隊負責.

具體對應到技術上, 也會根據產品線劃分, 將一個網站拆分成許多不同的應用程式, 每個應用程式獨立部署. 應用程式之間通過一個超連結建立關係(如在首頁上的navi link每個都會指向不同的應用程式地址), 也可以通過message queue進行資料分發, 當然最多的還是通過存取同一個資料存儲系統來構成一個關聯的完整系統, 此時的架構如下圖所示:

![](/files/-M523rZYNeC2O-HryxPE)

## 分散式服務

隨著業務拆分的越來越小, 儲存系統越來越龐大, 應用程式的整體複雜度呈指數增加, 部署跟維運都越來越困難. 由於所有應用程式要和所有資料庫系統連接, 在數萬台server規模的網站中, 這些連接的數目是server規模的平方, 導致資料庫的connection不夠用, 進而拒絕服務.

既然每一個應用系統都需要執行許多相同的業務操作, 譬如說使用者管理、商品管理等, 那麼可以將這些共用的業務抽出來, 獨立部署. 由這些可重用的業務連接資料庫, 提供共用業務服務, 而應用系統只需要管理用戶介面, 通過分散式服務調用這些共用業務服務完成具體業務操作. 這時候的架構可能如下所示:

![](/files/-M523rZ_rqBtCCg-mbav)

大型網站的架構演化到這裡, 基本上大多數的技術問題都得以解決, 諸如跨資料中心的即時資料同步和具體網站業務相關的問題也都可以通過組合改進現有技術架構解決.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://clu.gitbook.io/scrapbook/121-da-xing-wang-zhan-jia-gou-yan-hua-fa-zhan-li-cheng.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
