今日推荐:如何设计一个支撑数亿用户的系统
點擊上方“朱小廝的博客”,選擇“設為星標”
后臺回復"書",獲取
后臺回復“k8s”,可領取k8s資料
要設計出一套能支撐幾十億人的系統(tǒng)是很困難的。對于軟件架構師來說,這一直是一項很大的挑戰(zhàn),但是,從現(xiàn)在開始,看完我的文章,你就會覺得容易很多了。
下面是我在本文中提到的幾個話題:
從最簡單的開始:萬事合一。
可擴展性的藝術:縱向擴展,橫向擴展。
擴展關系型數(shù)據(jù)庫:主 - 從復制、主 - 主復制、聯(lián)合、分片、非規(guī)范化和 SQL 調(diào)優(yōu)。
使用哪種數(shù)據(jù)庫:NoSQL 還是 SQL?
先進概念:緩存、CDN、geoDNS 等。
在這篇文章里,我不打算談論諸如容錯、可靠性、高可用性等高性能計算的通用術語。
廢話不多說,言歸正傳。
1從頭開始
在下圖中,我要先設計一個有一些用戶的基本應用。最容易的方式是在一臺服務器上部署整個應用。我們中的大部分人可能都是這樣開始的。
一個網(wǎng)站(包括 API)在 Apache(或 Tomcat)等網(wǎng)絡服務器上運行。
一個 Oracle(或 MySQL)之類的數(shù)據(jù)庫。
我們在同一臺物理機上同時擁有 Web 服務器和數(shù)據(jù)庫服務器。
但是,當前的架構存在下列缺陷:
如果數(shù)據(jù)庫出現(xiàn)故障,則系統(tǒng)將失效。
一旦網(wǎng)絡服務器出現(xiàn)故障,則會導致整個系統(tǒng)的癱瘓。
在這種情況下,我們沒有故障轉(zhuǎn)移和冗余。如果一個服務器出現(xiàn)故障,所有的都將會失效。
使用 DNS 服務器來解析主機名和 IP 地址??
在上圖中,用戶(或客戶端)連接到 DNS 系統(tǒng),以獲得我們系統(tǒng)所在的服務器的互聯(lián)網(wǎng)協(xié)議(IP)地址。一旦獲得 IP 地址,請求就會直接發(fā)送到我們的系統(tǒng)。
每次訪問網(wǎng)站時,計算機都會執(zhí)行 DNS 查詢。
通常情況下,域名系統(tǒng)(DNS)服務器是作為托管公司提供的付費服務使用的,并不在你自己的服務器上運行。
2?可擴展性的藝術
由于很多原因,我們的系統(tǒng)可能需要進行擴展,例如數(shù)據(jù)量的增加、工作量的增加(如事務的數(shù)目),以及用戶的增加。
可擴展性一般是指添加更多的資源,在不影響用戶體驗的情況下處理更多的用戶、客戶機、數(shù)據(jù)、事務或請求。
我們必須決定怎樣才能擴大這個系統(tǒng)的規(guī)模。在這種情況下,有以下兩種類型的擴展:縱向擴展(scale up) 和橫向擴展(scale out)。
縱向擴展 vs 橫向擴展
縱向擴展:在現(xiàn)有服務器上增加更多的內(nèi)存和 CPU ?
這也被稱為“垂直擴展”,是指為了提高系統(tǒng)處理日益增長的負載的能力而使系統(tǒng)能夠最大限度地利用資源——例如,通過增加內(nèi)存和 CPU 來增加服務器的能力。
如果我們運行的服務器有 8G 的內(nèi)存,那么只要更換或者增加硬件,就可以輕松地提升到 32G,甚至 128G。
有很多方法可以進行縱向擴展,具體如下:
通過在 RAID 陣列中增加更多的硬盤來增加 I/O 容量。
通過切換到固態(tài)硬盤(SSD)來改善 I/O 訪問時間。
切換到具有更多處理器的服務器。
通過升級網(wǎng)絡接口或安裝額外的網(wǎng)絡接口來提高網(wǎng)絡吞吐量。
通過增加內(nèi)存來減少 I/O 操作。
對于小型系統(tǒng)來說,縱向擴展是一個很好的選擇,可以負擔得起硬件升級,但也存在一些嚴重的限制,具體如下:
“不可能在一臺服務器上增加無限的能力”。這主要取決于操作系統(tǒng)和服務器的內(nèi)存總線寬度。
給系統(tǒng)升級內(nèi)存時,必須關掉服務器,因此,如果系統(tǒng)只有一臺服務器,停機是不可避免的。
強大的機器往往要比流行的硬件昂貴很多。
縱向擴展不僅適用于硬件方面,也適用于軟件方面,例如,它包括優(yōu)化查詢和應用程序代碼。
相比之下,縱向減縮(scale down)是指從現(xiàn)有的服務器中移除現(xiàn)有的資源,如 CPU、內(nèi)存和磁盤。
您需要多臺服務器嗎???
當用戶數(shù)量不斷增加時,一臺服務器將無法滿足需求。我們需要考慮將一臺單獨的服務器分離到多臺服務器上。
當用戶數(shù)量不斷增加時,一臺服務器將無法滿足需求。
采用該架構有如下優(yōu)勢:
可對 Web 服務器進行不同于數(shù)據(jù)庫服務器的調(diào)優(yōu)。
網(wǎng)絡服務器需要更好的 CPU,而數(shù)據(jù)庫服務器需要更多的內(nèi)存。
為 Web 層和數(shù)據(jù)層提供單獨的服務器,允許它們彼此獨立地進行擴展。
橫向擴展:添加任意數(shù)量的硬件和軟件實體??
這也被稱為“水平擴展”,是指向資源池中添加更多的實體(如機器、服務等)。橫向擴展要比縱向擴展更難實現(xiàn),因為我們必須在建立一個系統(tǒng)之前就把這個問題考慮進去。
開始時,為了滿足最基本的需求,我們需要更多的服務器,因此橫向擴展最初往往花費更多,但是到了最后,我們將獲得更多的收益。我們需要權衡利弊。
服務器數(shù)量的增長意味著更多的資源需要維護。同時,還必須對系統(tǒng)代碼進行修改,以便實現(xiàn)在多臺服務器間進行并行和分配工作。
與此相反,橫向減縮(Scale in)指的是刪除現(xiàn)有服務器的過程。
3?使用負載均衡器來均衡所有節(jié)點上的流量?
負載均衡器是一種專門的硬件或軟件組件,它可以幫助分散流量到服務器集群,從而改善系統(tǒng)的響應能力和可用性,包括但不限于應用程序、網(wǎng)站或數(shù)據(jù)庫。
使用負載均衡器來均衡所有節(jié)點之間的流量。
負載均衡器一般都是在客戶端與服務器之間,接受傳入的網(wǎng)絡及應用程序的流量,并利用各種算法,將流量分配到多個后端服務器。所以,它也可以用于各種場合,比如 Web 服務器與數(shù)據(jù)庫服務器之間,以及客戶端和 Web 服務器之間。
HAProxy 和 Nginx 是目前比較受歡迎的開源負載均衡軟件。
負載均衡器技術是一種能夠改善系統(tǒng)可用性的容錯保護方法,具體如下:
如果服務器 1 脫機,則所有的流量將被路由到服務器 2 和服務器 3。網(wǎng)站就不會脫機。你還需要在服務器池中添加一個新的健康服務器來均衡負載。
當流量快速增長時,你只需要向網(wǎng)站服務器池添加更多的服務器,負載均衡器將為你路由流量。
負載均衡器通過不同的策略和任務分配算法對負載進行了最優(yōu)分配,具體如下:
循環(huán):在這種情況下,每個服務器按順序接收請求,類似于先進先出(FIFO)。
最少的連接數(shù):連接數(shù)最少的服務器將被引導到請求。
最快的響應時間:具有最快響應時間的服務器(最近或經(jīng)常)將被引導到請求。
加權:較強大的服務器將比較弱的服務器收到更多的請求加權策略。
IP 哈希:在這種情況下,計算客戶的 IP 地址的哈希值,將請求重定向到服務器。
在多個服務器之間均衡請求的最直接方法是使用一個硬件設備。
從共享 IP 中添加和刪除真正的服務器,將會立即發(fā)生。
負載均衡可以根據(jù)需要進行。
軟件負載均衡是硬件負載均衡器的一個廉價替代品。其操作于第 4 層(網(wǎng)絡層)和第 7 層(應用層)。
第 4 層:負載均衡器使用網(wǎng)絡層的 TCP 提供的信息。在這一層,它一般不會查看所請求的內(nèi)容,而是選擇一臺服務器。
第 7 層:請求可以根據(jù)查詢字符串、cookies 或我們選擇的任何頭的信息,以及包括源和目標地址在內(nèi)的常規(guī)層信息進行均衡。
4?擴展關系數(shù)據(jù)庫
對于一個簡單的系統(tǒng),我們可以通過 RDBMS,如 Oracle 或者 MySQL 來存儲數(shù)據(jù)項。然而,關系數(shù)據(jù)庫系統(tǒng)也存在著一些問題,尤其是在我們需要擴展的時候。
有很多技術可以擴展關系型數(shù)據(jù)庫:主 - 從復制、主 - 主復制、聯(lián)合、分片、非規(guī)范化和 SQL 調(diào)優(yōu)。
復制 通常指的是一種技術,可以讓我們在不同的機器上存儲同一數(shù)據(jù)的多個副本。
聯(lián)合(或功能分區(qū))將數(shù)據(jù)庫按功能進行劃分。
分片 是一種與分區(qū)相關的數(shù)據(jù)庫架構模式,它將數(shù)據(jù)的不同部分放到不同的服務器上,不同的用戶將訪問數(shù)據(jù)集的不同部分。
非規(guī)范化 試圖以犧牲一些寫入性能為代價來提高讀取性能,將數(shù)據(jù)寫入多個表中以避免昂貴的連接。
SQL 調(diào)優(yōu)。
主 - 從復制??
主 - 從復制技術使一個數(shù)據(jù)庫服務器(主服務器)的數(shù)據(jù)被復制到一個或多個其他數(shù)據(jù)庫服務器(從服務器),如下圖所示:
對主服務器進行的所有更新。
客戶端將連接到主服務器,并更新數(shù)據(jù)。
數(shù)據(jù)隨后會在從服務器上進行傳輸,直到所有的數(shù)據(jù)在服務器上都是一致的。
在實踐中,還是存在一些瓶頸。
如果主服務器由于某種原因宕機了,數(shù)據(jù)仍然可以通過從服務器獲得,但是將無法再進行新的寫入。
我們還需要一種新的算法,把一臺從服務器提升到主服務器。
下面是實現(xiàn)僅一臺服務器能處理更新請求的一些解決方案。
同步解決方案:只有當所有的服務器都接受了修改數(shù)據(jù)的事務(分布式事務)之后,才會被提交,因此,當發(fā)生故障切換時,數(shù)據(jù)不會丟失。
異步解決方案:提交 → 延遲 → 傳播到集群中的其他服務器,因此,當發(fā)生故障切換時,某些數(shù)據(jù)更新會丟失。
請記住,如果同步解決方案過慢,那就改成異步解決方案。
主 - 主復制??
每個數(shù)據(jù)庫服務器都可以在其他服務器被當作主服務器的同時充當主服務器。在某個時間點上,所有的這服務器都會同步,以確保它們的數(shù)據(jù)是正確的、最新的。
所有節(jié)點讀寫所有數(shù)據(jù)。
以下是主 - 主復制的一些優(yōu)勢:
當一臺主服務器發(fā)生故障時,其他數(shù)據(jù)庫服務器可以正常運行,并接替其工作。當數(shù)據(jù)庫服務器重新上線時,它將利用復制的方式趕上來。
主服務器可以位于幾個物理站點,也可以分布在網(wǎng)絡上。
受限于主服務器處理更新的能力。
聯(lián)合??
聯(lián)合(或功能分區(qū))將數(shù)據(jù)庫按功能劃分。例如,你可以有三個數(shù)據(jù)庫:Forum、users 和 products,而不是一個單一的單體數(shù)據(jù)庫,這樣就能降低對各個數(shù)據(jù)庫的讀寫流量,因此減少了復制滯后。
聯(lián)合按功能劃分數(shù)據(jù)庫。
數(shù)據(jù)庫越小,可以容納在內(nèi)存中的數(shù)據(jù)就越多,這反過來會導致緩存點擊率的增加,這是由于緩存命中的改進。因為不需要單一的中央主控器序列化寫操作,所以你可以進行并行寫入,這樣就可以提高吞吐量。
分片??
分片(也被稱為數(shù)據(jù)分區(qū)),是一種將大數(shù)據(jù)庫分成許多小部分的技術,這樣每個數(shù)據(jù)庫只能管理數(shù)據(jù)的一個子集。
在理想情況下,我們有不同的用戶都與不同的數(shù)據(jù)庫節(jié)點對話。它有助于提高系統(tǒng)的可管理性、性能、可用性和負載均衡。
每個用戶只需要和一個服務器對話,所以可以從該服務器得到快速的響應。
負載在服務器之間得到了很好的均衡——例如,如果我們有五個服務器,每個服務器只需要處理 20% 的負載。
在實踐中,有許多不同的技術可以將一個數(shù)據(jù)庫分解成多個小部分。
水平分區(qū)??
這種技術是將不同的行放到不同的表中。比如,如果我們在一個表中存儲用戶資料,我們可以決定將 ID 小于 1000 的用戶存儲在一個表中,而將 ID 大于 1001 小于 2000 的用戶存儲在另一個表中。
我們將不同的行放入不同的表中。
垂直分區(qū)??
在這種情況下,我們對數(shù)據(jù)進行劃分,將與特定特性相關的表存儲在它們自己的服務器上。例如,如果我們正在建立一個類似于 Instagram 的系統(tǒng)——需要存儲與用戶、他們上傳的照片以及他們所關注的人有關的數(shù)據(jù)——我們可以決定將用戶的資料信息放在一臺數(shù)據(jù)庫服務器上,好友列表放在另一臺服務器上,而照片放在第三臺服務器上。
我們將數(shù)據(jù)劃分,存儲與特定特性相關的表,并將其存儲在各自的服務器上。
基于目錄的分區(qū)??
解決這個問題的一個松散耦合的方法,就是創(chuàng)建一個查詢服務,它了解你當前的分區(qū)模式,并保持每個實體以及存儲在哪個數(shù)據(jù)庫分片的映射關系。
當數(shù)據(jù)存儲可能需要擴展到超出單個存儲節(jié)點的可用資源時,或者通過減少數(shù)據(jù)存儲中的爭用來提高性能時,我們可以使用這種方法。但請記住,分片技術存在以下一些常見問題:
數(shù)據(jù)庫連接變得更加昂貴,在某些情況下是不可行的。
分片會破壞數(shù)據(jù)庫的引用完整性。
數(shù)據(jù)庫模式的改變會變得非常昂貴。
數(shù)據(jù)分布不均勻,而且在分片上有大量負載。
非規(guī)范化??
非規(guī)范化的目的是提高讀取性能,但卻要犧牲一定的寫入性能。為了避免昂貴的連接,可以將數(shù)據(jù)的冗余副本寫入到多個表中。
一旦數(shù)據(jù)通過聯(lián)合和分片等技術變得分散,管理跨數(shù)據(jù)中心的連接將會進一步增加復雜性。非規(guī)范化可以避免需要如此復雜的連接。
在大多數(shù)系統(tǒng)中,讀取操作的次數(shù)遠遠多于寫入操作,大約是 100:1,甚至是 1000:1。導致讀取復雜數(shù)據(jù)庫連接可能會非常昂貴,而且會耗費很多時間在磁盤上。
有些 RDBMS,像 PostgreSQL 和 Oracle 都支持物化視圖,它們可以處理存儲冗余數(shù)據(jù),并使冗余副本保持一致。
Facebook 的 Ryan Mack 在其出色的文章《建立時間表:利用非規(guī)范化的力量擴大規(guī)模來保存你的生活故事》(Building Timeline: Scaling up to hold your life story)中分享了很多時間表自身的實現(xiàn)故事。
5?使用哪個數(shù)據(jù)庫?
在數(shù)據(jù)庫領域,主要有兩種類型的解決方案。SQL 與 NoSQL。它們的構建方式、存儲信息的類型以及存儲方式都有所不同。
SQL??
關系型數(shù)據(jù)庫以行和列的形式存儲數(shù)據(jù)。每一行包含一個實體的所有信息,每一列包含所有獨立的數(shù)據(jù)點。
目前最受歡迎的關系型數(shù)據(jù)庫是 MySQL、Oracle、MS SQL Server、SQLite、Postgres 和 MariaDB。
NoSQL??
它也被稱為非關系型數(shù)據(jù)庫。這些數(shù)據(jù)庫一般分為五大類別:Key-Value、Graph、Column、Document 和 Blob 存儲。
鍵值存儲??
數(shù)據(jù)被存儲在一個鍵值對的數(shù)組中。key(鍵)是一個與 value(值)相連的屬性名稱。
知名的鍵值存儲有 Redis、Voldemort 和 Dynamo。
文檔數(shù)據(jù)庫??
在這些數(shù)據(jù)庫中,數(shù)據(jù)被存儲在文檔中(而不是表格中的行和列),這些文檔被分組在集合中。每個文檔都可能是截然不同的結構。
文檔數(shù)據(jù)庫包括 CouchDB 和 MongoDB。
寬列式數(shù)據(jù)庫??
在列式數(shù)據(jù)庫中,我們沒有“表”,而是有列族,它們是行的容器。與關系型數(shù)據(jù)庫不同,我們不必事先了解所有的列,也不必要求每一行的列數(shù)目都相同。
列式數(shù)據(jù)庫最適合分析大型數(shù)據(jù)集,著名的有 Cassandra 和 HBase。
圖數(shù)據(jù)庫??
這些數(shù)據(jù)庫用于存儲數(shù)據(jù),其關系最好用圖來表示。數(shù)據(jù)被保存在帶有節(jié)點(實體)、屬性(關于實體的信息)和線(實體之間的連接)的圖結構中。
圖數(shù)據(jù)庫的例子包括 Neo4J 和 InfiniteGraph。
Blob 數(shù)據(jù)庫??
Blob 更像是文件的鍵 / 值存儲,可以通過 Amazon S3、Windows Azure Blob Storage、Google Cloud Storage、Rackspace Cloud Files 或 OpenStack Swift 等 API 訪問。
如何選擇要使用的數(shù)據(jù)庫???
當涉及數(shù)據(jù)庫技術時,沒有放之四海而皆準的解決方案。這就是為什么許多企業(yè)同時依賴 SQL 和 NoSQL 數(shù)據(jù)庫來滿足不同的需求。
請看下面我畫的思維導圖!
使用哪個數(shù)據(jù)庫?
6?橫向擴展 Web 層
我們已經(jīng)擴展了數(shù)據(jù)層,現(xiàn)在我們也需要擴展 Web 層。為了做到這一點,我們需要將用戶會話的數(shù)據(jù)(狀態(tài))移出 Web 層,將其存儲在數(shù)據(jù)庫中,如關系型數(shù)據(jù)庫或 NoSQL。這也被稱為無狀態(tài)架構。
無狀態(tài)系統(tǒng)很簡單。
不要使用有狀態(tài)架構。?
由于狀態(tài)的實現(xiàn)會限制可擴展性。降低可用性和提高成本,所以我們需要盡可能地選擇無狀態(tài)架構。
在上面的場景中,由于可以為最優(yōu)的請求處理選擇任意服務器,因此負載均衡器能夠可以達到最高的效率。
7?先進概念
緩存??
負載均衡能夠幫助你橫向擴展越來越多的服務器,但緩存可以讓你更好地利用現(xiàn)有的資源,從而更快速地向下一個請求提供數(shù)據(jù)。
如果數(shù)據(jù)不在緩存中,就從數(shù)據(jù)庫中獲取,然后保存到緩存中,再從緩存中讀取。
我們可以在服務器中添加緩存,避免從服務器中直接讀取網(wǎng)頁或數(shù)據(jù),從而降低了服務器的響應時間及負載。這使得我們的應用程序更加易于擴展。
緩存可以被用于許多層,例如數(shù)據(jù)庫層、Web 服務器層和網(wǎng)絡層。
內(nèi)容分發(fā)網(wǎng)絡 (CDN )??
CDN 服務器保存內(nèi)容(如圖像、網(wǎng)頁等)的緩存副本,并從最近的位置提供服務。
CDN 的使用可以提高用戶的頁面加載時間,因為數(shù)據(jù)是在離它最近的地方檢索的。這也有助于提高內(nèi)容的可用性,因為它被存儲在多個地點。
使用 CDN 改善了用戶的頁面加載時間,因為數(shù)據(jù)是在最接近它的地方被檢索到的。
CDN 服務器向我們的網(wǎng)絡服務器發(fā)出請求,以驗證被緩存的內(nèi)容,并在需要時更新它們。被緩存的內(nèi)容通常是靜態(tài)的,如 HTML 頁面、圖像、JavaScript 文件、CSS 文件等。
走向全球??
隨著你的應用程序在全球范圍內(nèi)推廣,你將會在全球范圍內(nèi)建立和運營數(shù)據(jù)中心,使你的產(chǎn)品每天 24 小時、每周 7 天保持運行。收到的請求將被路由到基于 GeoDNS 的“最佳”數(shù)據(jù)中心。
當你的應用程序走向全球時……
GeoDNS 是一項 DNS 服務,它可以將一個域名按照用戶所在的位置解析為 IP 地址。來自亞洲的客戶端可以得到與來自歐洲客戶端的不同 IP 地址。
把它整合在一起??
通過迭代應用所有這些技術,我們可以輕松地將系統(tǒng)擴展到 1 億多用戶,如無狀態(tài)架構、應用負載均衡器、盡可能多地使用緩存數(shù)據(jù)、支持多個數(shù)據(jù)中心、在 CDN 上托管靜態(tài)資產(chǎn)、通過分片擴展你的數(shù)據(jù)層,諸如此類。
擴展是一個迭代的過程。
8?后面會討論哪些話題?
有很多方法可以提高可擴展性和高性能,如下所示:
分片和復制技術相結合。
長輪詢 vs Websockets vs 服務器發(fā)送事件。
索引和代理。
SQL 調(diào)優(yōu)。
彈性計算。
作者:Anh T. Dang
來源:InfoQ 架構頭條
參考鏈接:
https://httpd.apache.org
http://tomcat.apache.org
https://www.oracle.com/database/
https://www.mysql.com
https://en.wikipedia.org/wiki/Domain_Name_System
https://www.facebook.com/note.php?note_id=10150468255628920
原文鏈接:
https://levelup.gitconnected.com/how-to-design-a-system-to-scale-to-your-first-100-million-users-4450a2f9703d?
想知道更多?掃描下面的二維碼關注我后臺回復"技術",加入技術群 后臺回復“k8s”,可領取k8s資料【精彩推薦】ClickHouse到底是什么?為什么如此牛逼!
原來ElasticSearch還可以這么理解
面試官:InnoDB中一棵B+樹可以存放多少行數(shù)據(jù)?
架構之道:分離業(yè)務邏輯和技術細節(jié)
星巴克不使用兩階段提交
面試官:Redis新版本開始引入多線程,談談你的看法?
喜馬拉雅自研網(wǎng)關架構演進過程
收藏:存儲知識全面總結
微博千萬級規(guī)模高性能高并發(fā)的網(wǎng)絡架構設計
總結
以上是生活随笔為你收集整理的今日推荐:如何设计一个支撑数亿用户的系统的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 再见了kafka2.0时代,去掉了zk的
- 下一篇: 重新深入理解零拷贝技术