圖解分布式系統架構演進之路


0、介紹

本篇文章是在我看完《從零開始學架構》之后,以架構演變為主線,梳理了一下演變過程中出現的問題以及解決方案,文章中引用了這本書的一些內容和圖片

分布式和集群的概念經常被搞混,現在一句話讓你明白兩者的區別。

分布式:一個業務拆分成多個子業務,部署在不同的服務器上
集群:同一個業務,部署在多個服務器上

例如:電商系統可以拆分成商品,訂單,用戶等子系統。這就是分布式,而為了應對並發,同時部署好幾個用戶系統,這就是集群

1、單應用架構

2、應用服務器和數據服務器分離

單機負載越來越來,所以要將應用服務器和數據庫服務器分離

 

 

3、應用服務器做集群

每個系統的處理能力是有限的,為了提高並發訪問量,需要對應用服務器做集群

 

這時會涉及到兩個問題:

  1. 負載均衡

  2. session共享

負載均衡就是將請求均衡地分配到多個系統上,常見的技術有如下幾種

DNS

DNS是最簡單也是最常見的負載均衡方式,一般用來實現地理級別的均衡。例如,北方的用戶訪問北京的機房,南方的用戶訪問廣州的機房。一般不會使用DNS來做機器級別的負載均衡,因為太耗費IP資源了。例如,百度搜索可能要10000台以上的機器,不可能將這么多機器全部配置公網IP,然后用DNS來做負載均衡。

Nginx&LVS&F5

DNS是用於實現地理級別的負載均衡,而Nginx&LVS&F5用於同一地點內機器級別的負載均衡。其中Nginx是軟件的7層負載均衡,LVS是內核的4層負載均衡,F5是硬件做4層負載均衡,性能從低到高位Nginx<LVS<F5

下圖形象的展示了一個實際請求過程中,地理級別的負載均衡和機器級別的負載均衡是如何分工和結合的,其中粗線是地理級別的負載均衡,細線是機器級別的負載均衡,實線代表最終的路由路徑

session共享

session共享就是用戶在A服務器登錄,結果查看購物車時,請求發送到了B服務器,因此用戶的session存在A服務器上,所以當請求發送到B服務器上時,會認為用戶沒有登錄

目前解決session跨域共享問題有如下幾種方式

  1. session sticky

    將請求都落到同一個服務器上,如Nginx的url hash

  2. session replication

    session復制,每台服務器都保存一份相同的session

  3. session 集中存儲

    存儲在db、 存儲在緩存服務器 (redis)

  4. cookie (主流)

    將信息存在加密后的cookie中

4、數據庫讀寫分離

搭建數據庫主從集群,實現數據庫讀寫分離,改善數據庫負載壓力

數據庫讀寫分離的基本實現如下

  1. 數據庫服務器搭建主從集群,一主一從,一主多從都可以

  2. 數據庫主機負責讀寫操作,從機只負責讀操作

  3. 數據庫主機通過復制將數據同步到從機,每台數據庫服務器都存儲了所有的業務數據

  4. 業務服務器將寫操作分給數據庫主機,將讀操作分給數據庫從機

實現方式

讀寫分離需要將讀/寫操作區分開來,然后訪問不同的數據庫服務器;分庫分表需要根據不同的數據訪問不同的數據庫服務器,兩者本質上都是一種分配機制,即將不同的SQL語句發送到不同的數據庫服務器。

讀寫分離,包括后面要提到的分庫分表的實現方式有兩種:

  1. 程序代碼封裝

  2. 中間件封裝

程序代碼封裝指在代碼中抽象一個數據訪問層來實現讀寫分離,分庫分表

中間件封裝指的是獨立一套系統出來,實現讀寫分離和分庫分表操作,如我們熟悉的MySQL Router和Mycat等

 5、引入搜索引擎來查詢

傳統的關系型數據庫通過索引來達到快速查詢的目的,但是在全文搜索的業務場景下,索引也無能為力,主要體現在如下幾點:

  1. 全文搜索的條件可以隨意排列組合,如果通過索引來滿足,則索引的數量會非常多

  2. 全文搜索的模糊匹配方式,索引無法滿足,只能用like查詢,而like查詢是整表掃描,效率非常低

目前主要有Elasticsearch與Solr。Solr 是傳統搜索應用的有力解決方案,但 Elasticsearch 更適用於新興的實時搜索應用。

6、增加緩存

為了應對流量持續增加,必須增加緩存

常見的方式有如下幾種:

Redis與Memcached

以我們常見的Mybatis為例,很容易和Redis與Memcached整合起來,緩存已經查詢過的SQL,因為Mybatis知道自己不擅長緩存,所以提供了接口讓這些緩存工具進行整合

CDN

CDN是為了解決用戶網絡訪問時的“最后一公里”效應,本質上是一種“以空間換空間”的加速策略,即將內容緩存在離用戶最近的地方,用戶訪問的是緩存的內容,而不是站點實時的內容。

7、分庫分表

讀寫分離分散了數據庫讀寫操作的壓力,但沒有分散存儲壓力,當數據量達到千萬甚至上億條的時候,單台服務器的存儲能力會成為系統的瓶頸。常見的分散存儲的方法有分庫和分表兩大類

 

業務分庫

業務分庫指的是按照業務模塊將數據分散到不同的數據庫服務器。例如,一個簡單的電商網站,包括商品,訂單,用戶三個業務模塊,我們可以將商品數據,訂單數據,用戶數據,分開放到3台不同的數據庫服務器上,而不是將所有數據都放在一台數據庫服務器上

當然業務分庫也會帶來新的問題:

  1. join操作問題:業務分庫后,原本在同一個數據庫中的表分散到不同數據庫中,導致無法使用SQL的join查詢

  2. 事務問題:原本在同一個數據庫中不同的表可以在同一個事務中修改,業務分庫后,表分散到不同數據庫中,無法通過事務統一修改

  3. 成本問題:業務分庫同時也帶來了成本的代價,本來1台服務器搞定的事情,現在要3台,如果考慮備份,那就是2台變成6台

分表

表單數據拆分有兩種方式,垂直分表水平分表

垂直分表:垂直分表適合將表中某些不常用且占了大量空間的列拆分出去。如上圖的nickname和description字段不常用,就可以將這個字段獨立到另外一張表中,這樣在查詢name時,就能帶來一定的性能提升

水平分表:水平分表適合表行數特別大的表,如果單表行數超過5000萬就必須進行分表,這個數字可以作為參考,但並不是絕對標准,關鍵還是要看表的訪問性能

水平分表后,某條數據具體屬於哪個切分后的子表,需要增加路由算法進行計算,常見的路由算法

范圍路由:選取有序的數據列(例如,整型,時間戳等)作為路由條件,不同分段分散到不同的數據庫表中。以最常見的用戶ID為例,路由算法可以按照1000000的范圍大小進行分段,1-999999放到數據庫1的表中,1000000-1999999放到數據庫2的表中,以此類推

Hash路由:選取某個列(或者某幾個列組合也可以)的值進行Hash運算,然后根據Hash結果分散到不同的數據庫表中。同樣以用戶Id為例,假如我們一開始就規划了10個數據庫表,路由算法可以簡單地用user_id%10的值來表示數據所屬的數據庫表編號,ID為985的用戶放到編號為5的子表中,ID為10086的用戶放到編號為6的子表中。

配置路由:配置路由就是路由表,用一張獨立的表來記錄路由信息,同樣以用戶ID為例,我們新增一張user_router表,這個表包含user_id和table_id兩列,根據user_id就可以查詢對應的table_id

8、應用拆分/微服務

隨着業務的發展,業務越來越多,應用的壓力越來越大。工程規模也越來越龐大。這個時候就可以考慮將應用拆分,按照領域模型將我們的商品,訂單,用戶分拆成子系統。

這樣拆分以后,可能會有一些相同的代碼,比如訂單模塊有對用戶數據的查詢,用戶模塊中肯定也有對用戶數據的查詢。這些相同的代碼和模塊一定要抽象出來。這樣有利於維護和管理。這時可以將模塊變為一個個服務,模塊之間互相調用來獲取數據,系統就變成一個微服務了。

 

服務拆分以后,服務之間的通信可以通過RPC技術,比較典型的有:Webservice、Hessian、HTTP、RMI等。如當前的Dubbo和Spring Cloud都是目前比較流行的微服務框架。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM