來源:levelup.gitconnected.com/how-to-design-a-system-to-scale-to-your-first-100-million-users-4450a2f9703d 1 從頭開始 2 可擴展性的藝術 3 使用負載均衡器來均衡所有節點上的流量 4 擴展關係數 ...
來源:levelup.gitconnected.com/how-to-design-a-system-to-scale-to-your-first-100-million-users-4450a2f9703d
- 1 從頭開始
- 2 可擴展性的藝術
- 3 使用負載均衡器來均衡所有節點上的流量
- 4 擴展關係資料庫
- 5 使用哪個資料庫?
- 6 橫向擴展 Web 層
- 7 先進概念
- 8 後面會討論哪些話題?
要設計出一套能支撐幾十億人的系統是很困難的。對於軟體架構師來說,這一直是一項很大的挑戰,但是,從現在開始,看完我的文章,你就會覺得容易很多了。
下麵是我在本文中提到的幾個話題:
- 從最簡單的開始:萬事合一。
- 可擴展性的藝術:縱向擴展,橫向擴展。
- 擴展關係型資料庫:主 - 從複製、主 - 主複製、聯合、分片、非規範化和 SQL 調優。
- 使用哪種資料庫:NoSQL 還是 SQL?
- 先進概念:緩存、CDN、geoDNS 等。
在這篇文章里,我不打算談論諸如容錯、可靠性、高可用性等高性能計算的通用術語。
廢話不多說,言歸正傳。
1 從頭開始
在下圖中,我要先設計一個有一些用戶的基本應用。
最容易的方式是在一臺伺服器上部署整個應用。我們中的大部分人可能都是這樣開始的。
- 一個網站(包括 API)在 Apache(或 Tomcat)等網路伺服器上運行。
- 一個 Oracle(或 MySQL)之類的資料庫。
我們在同一
但是,當前的架構存在下列缺陷:
- 如果資料庫出現故障,則系統將失效。
- 一旦網路伺服器出現故障,則會導致整個系統的癱瘓。
在這種情況下,我們沒有故障轉移和冗餘。如果一個伺服器出現故障,所有的都將會失效。
推薦一個開源免費的 Spring Boot 實戰項目:
使用 DNS 伺服器來解析主機名和 IP 地址
在上圖中,用戶(或客戶端)連接到 DNS 系統,以獲得我們系統所在的伺服器的互聯網協議(IP)地址。一旦獲得 IP 地址,請求就會直接發送到我們的系統。
每次訪問網站時,電腦都會執行 DNS 查詢。
通常情況下,功能變數名稱系統(DNS)伺服器是作為托管公司提供的付費服務使用的,並不在你自己的伺服器上運行。
2 可擴展性的藝術
由於很多原因,我們的系統可能需要進行擴展,例如數據量的增加、工作量的增加(如事務的數目),以及用戶的增加。
可擴展性一般是指添加更多的資源,在不影響用戶體驗的情況下處理更多的用戶、客戶機、數據、事務或請求。
我們必須決定怎樣才能擴大這個系統的規模。在這種情況下,有以下兩種類型的擴展:縱向擴展
(scale up) 和橫向擴展
(scale out)。
縱向擴展:在現有伺服器上增加更多的記憶體和 CPU
這也被稱為“垂直擴展”,是指為了提高系統處理日益增長的負載的能力而使系統能夠最大限度地利用資源——例如,通過增加記憶體和 CPU 來增加伺服器的能力。
如果我們運行的伺服器有 8G 的記憶體,那麼只要更換或者增加硬體,就可以輕鬆地提升到 32G,甚至 128G。
有很多方法可以進行縱向擴展,具體如下:
- 通過在 RAID 陣列中增加更多的硬碟來增加 I/O 容量。
- 通過切換到固態硬碟(SSD)來改善 I/O 訪問時間。
- 切換到具有更多處理器的伺服器。
- 通過升級網路介面或安裝額外的網路介面來提高網路吞吐量。
- 通過增加記憶體來減少 I/O 操作。
對於小型系統來說,縱向擴展是一個很好的選擇,可以負擔得起硬體升級,但也存在一些嚴重的限制,具體如下:
- “不可能在一臺伺服器上增加無限的能力”。這主要取決於操作系統和伺服器的記憶體匯流排寬度。
- 給系統升級記憶體時,必須關掉伺服器,因此,如果系統只有一臺伺服器,停機是不可避免的。
- 強大的機器往往要比流行的硬體昂貴很多。
縱向擴展不僅適用於硬體方面,也適用於軟體方面,例如,它包括優化查詢和應用程式代碼。
相比之下,縱向減縮(scale down)是指從現有的伺服器中移除現有的資源,如 CPU、記憶體和磁碟。
您需要多台伺服器嗎?
當用戶數量不斷增加時,一臺伺服器將無法滿足需求。我們需要考慮將一臺單獨的伺服器分離到多台伺服器上。
採用該架構有如下優勢:
- 可對 Web 伺服器進行不同於資料庫伺服器的調優。
- 網路伺服器需要更好的 CPU,而資料庫伺服器需要更多的記憶體。
- 為 Web 層和數據層提供單獨的伺服器,允許它們彼此獨立地進行擴展。
橫向擴展:添加任意數量的硬體和軟體實體
這也被稱為“水平擴展”,是指向資源池中添加更多的實體(如機器、服務等)。橫向擴展要比縱向擴展更難實現,因為我們必須在建立一個系統之前就把這個問題考慮進去。
開始時,為了滿足最基本的需求,我們需要更多的伺服器,因此橫向擴展最初往往花費更多,但是到了最後,我們將獲得更多的收益。我們需要權衡利弊。
伺服器數量的增長意味著更多的資源需要維護。同時,還必須對系統代碼進行修改,以便實現在多台伺服器間進行並行和分配工作。
與此相反,橫向減縮(Scale in)指的是刪除現有伺服器的過程。
3 使用負載均衡器來均衡所有節點上的流量
負載均衡器是一種專門的硬體或軟體組件,它可以幫助分散流量到伺服器集群,從而改善系統的響應能力和可用性,包括但不限於應用程式、網站或資料庫。
負載均衡器一般都是在客戶端與伺服器之間,接受傳入的網路及應用程式的流量,並利用各種演算法,將流量分配到多個後端伺服器。所以,它也可以用於各種場合,比如 Web 伺服器與資料庫伺服器之間,以及客戶端和 Web 伺服器之間。
HAProxy 和 Nginx 是目前比較受歡迎的開源負載均衡軟體。
負載均衡器技術是一種能夠改善系統可用性的容錯保護方法,具體如下:
- 如果伺服器 1 離線,則所有的流量將被路由到伺服器 2 和伺服器 3。網站就不會離線。你還需要在伺服器池中添加一個新的健康伺服器來均衡負載。
- 當流量快速增長時,你只需要向網站伺服器池添加更多的伺服器,負載均衡器將為你路由流量。
負載均衡器通過不同的策略和任務分配演算法對負載進行了最優分配,具體如下:
- 迴圈 :在這種情況下,每個伺服器按順序接收請求,類似於先進先出(FIFO)。
- 最少的連接數 :連接數最少的伺服器將被引導到請求。
- 最快的響應時間 :具有最快響應時間的伺服器(最近或經常)將被引導到請求。
- 加權 :較強大的伺服器將比較弱的伺服器收到更多的請求加權策略。
- IP 哈希 :在這種情況下,計算客戶的 IP 地址的哈希值,將請求重定向到伺服器。
在多個伺服器之間均衡請求的最直接方法是使用一個硬體設備。
- 從共用 IP 中添加和刪除真正的伺服器,將會立即發生。
- 負載均衡可以根據需要進行。
軟體負載均衡是硬體負載均衡器的一個廉價替代品。其操作於第 4 層(網路層)和第 7 層(應用層)。
- 第 4 層:負載均衡器使用網路層的 TCP 提供的信息。在這一層,它一般不會查看所請求的內容,而是選擇一臺伺服器。
- 第 7 層:請求可以根據查詢字元串、cookies 或我們選擇的任何頭的信息,以及包括源和目標地址在內的常規層信息進行均衡。
4 擴展關係資料庫
對於一個簡單的系統,我們可以通過 RDBMS,如 Oracle 或者 MySQL 來存儲數據項。然而,關係資料庫系統也存在著一些問題,尤其是在我們需要擴展的時候。
有很多技術可以擴展關係型資料庫:主 - 從複製、主 - 主複製、聯合、分片、非規範化和 SQL 調優。
- 複製 通常指的是一種技術,可以讓我們在不同的機器上存儲同一數據的多個副本。
- 聯合 (或功能分區)將資料庫按功能進行劃分。
- 分片 是一種與分區相關的資料庫架構模式,它將數據的不同部分放到不同的伺服器上,不同的用戶將訪問數據集的不同部分。
- 非規範化 試圖以犧牲一些寫入性能為代價來提高讀取性能,將數據寫入多個表中以避免昂貴的連接。
- SQL 調優。
主 - 從複製
主 - 從複製技術使一個資料庫伺服器(主伺服器)的數據被覆制到一個或多個其他資料庫伺服器(從伺服器),如下圖所示:
- 客戶端將連接到主伺服器,並更新數據。
- 數據隨後會在從伺服器上進行傳輸,直到所有的數據在伺服器上都是一致的。
在實踐中,還是存在一些瓶頸。
- 如果主伺服器由於某種原因宕機了,數據仍然可以通過從伺服器獲得,但是將無法再進行新的寫入。
- 我們還需要一種新的演算法,把一臺從伺服器提升到主伺服器。
下麵是實現僅一臺伺服器能處理更新請求的一些解決方案。
- 同步解決方案 :只有當所有的伺服器都接受了修改數據的事務(分散式事務)之後,才會被提交,因此,當發生故障切換時,數據不會丟失。
- 非同步解決方案 :提交 → 延遲 → 傳播到集群中的其他伺服器,因此,當發生故障切換時,某些數據更新會丟失。
請記住,如果同步解決方案過慢,那就改成非同步解決方案。
主 - 主複製
每個資料庫伺服器都可以在其他伺服器被當作主伺服器的同時充當主伺服器。在某個時間點上,所有的這伺服器都會同步,以確保它們的數據是正確的、最新的。
以下是主 - 主複製的一些優勢:
- 當一臺主伺服器發生故障時,其他資料庫伺服器可以正常運行,並接替其工作。當資料庫伺服器重新上線時,它將利用複製的方式趕上來。
- 主伺服器可以位於幾個物理站點,也可以分佈在網路上。
- 受限於主伺服器處理更新的能力。
聯合
聯合(或功能分區)將資料庫按功能劃分。例如,你可以有三個資料庫:Forum、users 和 products,而不是一個單一的單體資料庫,這樣就能降低對各個資料庫的讀寫流量,因此減少了複製滯後。
資料庫越小,可以容納在記憶體中的數據就越多,這反過來會導致緩存點擊率的增加,這是由於緩存命中的改進。因為不需要單一的中央主控器序列化寫操作,所以你可以進行並行寫入,這樣就可以提高吞吐量。
分片
分片(也被稱為數據分區),是一種將大資料庫分成許多小部分的技術,這樣每個資料庫只能管理數據的一個子集。
在理想情況下,我們有不同的用戶都與不同的資料庫節點對話。它有助於提高系統的可管理性、性能、可用性和負載均衡。
- 每個用戶只需要和一個伺服器對話,所以可以從該伺服器得到快速的響應。
- 負載在伺服器之間得到了很好的均衡——例如,如果我們有五個伺服器,每個伺服器只需要處理 20% 的負載。
在實踐中,有許多不同的技術可以將一個資料庫分解成多個小部分。
水平分區
這種技術是將不同的行放到不同的表中。比如,如果我們在一個表中存儲用戶資料,我們可以決定將 ID 小於 1000 的用戶存儲在一個表中,而將 ID 大於 1001 小於 2000 的用戶存儲在另一個表中。
垂直分區
在這種情況下,我們對數據進行劃分,將與特定特性相關的表存儲在它們自己的伺服器上。例如,如果我們正在建立一個類似於 Instagram 的系統——需要存儲與用戶、他們上傳的照片以及他們所關註的人有關的數據——我們可以決定將用戶的資料信息放在一臺資料庫伺服器上,好友列表放在另一臺伺服器上,而照片放在第三台伺服器上。
我們將數據劃分,存儲與特定特性相關的表,並將其存儲在各自的伺服器上。
基於目錄的分區
解決這個問題的一個鬆散耦合的方法,就是創建一個查詢服務,它瞭解你當前的分區模式,並保持每個實體以及存儲在哪個資料庫分片的映射關係。
當數據存儲可能需要擴展到超出單個存儲節點的可用資源時,或者通過減少數據存儲中的爭用來提高性能時,我們可以使用這種方法。但請記住,分片技術存在以下一些常見問題:
- 資料庫連接變得更加昂貴,在某些情況下是不可行的。
- 分片會破壞資料庫的引用完整性。
- 資料庫模式的改變會變得非常昂貴。
- 數據分佈不均勻,而且在分片上有大量負載。
非規範化
非規範化的目的是提高讀取性能,但卻要犧牲一定的寫入性能。為了避免昂貴的連接,可以將數據的冗餘副本寫入到多個表中。
一旦數據通過聯合和分片等技術變得分散,管理跨數據中心的連接將會進一步增加複雜性。非規範化可以避免需要如此複雜的連接。
在大多數系統中,讀取操作的次數遠遠多於寫入操作,大約是 100:1,甚至是 1000:1。導致讀取複雜資料庫連接可能會非常昂貴,而且會耗費很多時間在磁碟上。
有些 RDBMS,像 PostgreSQL 和 Oracle 都支持物化視圖,它們可以處理存儲冗餘數據,並使冗餘副本保持一致。
Facebook 的 Ryan Mack 在其出色的文章《建立時間表:利用非規範化的力量擴大規模來保存你的生活故事》(Building Timeline: Scaling up to hold your life story)中分享了很多時間表自身的實現故事。
5 使用哪個資料庫?
在資料庫領域,主要有兩種類型的解決方案。SQL 與 NoSQL。它們的構建方式、存儲信息的類型以及存儲方式都有所不同。
SQL
關係型資料庫以行和列的形式存儲數據。每一行包含一個實體的所有信息,每一列包含所有獨立的數據點。
目前最受歡迎的關係型資料庫是 MySQL、Oracle、MS SQL Server、SQLite、Postgres 和 MariaDB。
NoSQL
它也被稱為非關係型資料庫。這些資料庫一般分為五大類別:Key-Value、Graph、Column、Document 和 Blob 存儲。
鍵值存儲
數據被存儲在一個鍵值對的數組中。key
(鍵)是一個與 value
(值)相連的屬性名稱。
知名的鍵值存儲有 Redis、Voldemort 和 Dynamo。
文檔資料庫
在這些資料庫中,數據被存儲在文檔中(而不是表格中的行和列),這些文檔被分組在集合中。每個文檔都可能是截然不同的結構。
文檔資料庫包括 CouchDB 和 MongoDB。
寬列式資料庫
在列式資料庫中,我們沒有“表”,而是有列族,它們是行的容器。與關係型資料庫不同,我們不必事先瞭解所有的列,也不必要求每一行的列數目都相同。
列式資料庫最適合分析大型數據集,著名的有 Cassandra 和 HBase。
圖資料庫
這些資料庫用於存儲數據,其關係最好用圖來表示。數據被保存在帶有節點(實體)、屬性(關於實體的信息)和線(實體之間的連接)的圖結構中。
圖資料庫的例子包括 Neo4J 和 InfiniteGraph。
Blob 資料庫
Blob 更像是文件的鍵 / 值存儲,可以通過 Amazon S3、Windows Azure Blob Storage、Google Cloud Storage、Rackspace Cloud Files 或 OpenStack Swift 等 API 訪問。
如何選擇要使用的資料庫?
當涉及資料庫技術時,沒有放之四海而皆準的解決方案。這就是為什麼許多企業同時依賴 SQL 和 NoSQL 資料庫來滿足不同的需求。
請看下麵我畫的思維導圖!
6 橫向擴展 Web 層
我們已經擴展了數據層,現在我們也需要擴展 Web 層。為了做到這一點,我們需要將用戶會話的數據(狀態)移出 Web 層,將其存儲在資料庫中,如關係型資料庫或 NoSQL。這也被稱為無狀態架構。
不要使用有狀態架構。
由於狀態的實現會限制可擴展性。降低可用性和提高成本,所以我們需要儘可能地選擇無狀態架構。
在上面的場景中,由於可以為最優的請求處理選擇任意伺服器,因此負載均衡器能夠可以達到最高的效率。
7 先進概念
緩存
負載均衡能夠幫助你橫向擴展越來越多的伺服器,但緩存可以讓你更好地利用現有的資源,從而更快速地向下一個請求提供數據。
如果數據不在緩存中,就從資料庫中獲取,然後保存到緩存中,再從緩存中讀取。
我們可以在伺服器中添加緩存,避免從伺服器中直接讀取網頁或數據,從而降低了伺服器的響應時間及負載。這使得我們的應用程式更加易於擴展。
緩存可以被用於許多層,例如資料庫層、Web 伺服器層和網路層。
內容分髮網絡 (CDN )
CDN 伺服器保存內容(如圖像、網頁等)的緩存副本,並從最近的位置提供服務。
CDN 的使用可以提高用戶的頁面載入時間,因為數據是在離它最近的地方檢索的。這也有助於提高內容的可用性,因為它被存儲在多個地點。
使用 CDN 改善了用戶的頁面載入時間,因為數據是在最接近它的地方被檢索到的。
CDN 伺服器向我們的網路伺服器發出請求,以驗證被緩存的內容,併在需要時更新它們。被緩存的內容通常是靜態的,如 HTML 頁面、圖像、JavaScript 文件、CSS 文件等。
走向全球
隨著你的應用程式在全球範圍內推廣,你將會在全球範圍內建立和運營數據中心,使你的產品每天 24 小時、每周 7 天保持運行。收到的請求將被路由到基於 GeoDNS 的“最佳”數據中心。
GeoDNS 是一項 DNS 服務,它可以將一個功能變數名稱按照用戶所在的位置解析為 IP 地址。來自亞洲的客戶端可以得到與來自歐洲客戶端的不同 IP 地址。
把它整合在一起
通過迭代應用所有這些技術,我們可以輕鬆地將系統擴展到 1 億多用戶,如無狀態架構、應用負載均衡器、儘可能多地使用緩存數據、支持多個數據中心、在 CDN 上托管靜態資產、通過分片擴展你的數據層,諸如此類。
8 後面會討論哪些話題?
有很多方法可以提高可擴展性和高性能,如下所示:
- 分片和複製技術相結合。
- 長輪詢 vs Websockets vs 伺服器發送事件。
- 索引和代理。
- SQL 調優。
- 彈性計算。
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
覺得不錯,別忘了隨手點贊+轉發哦!