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

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


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


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


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


session共享

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

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

  1. session sticky
  2. 將請求都落到同一個服務器上,如Nginx的url hash
  3. session replication
  4. session複製,每臺服務器都保存一份相同的session
  5. session 集中存儲
  6. 存儲在db、 存儲在緩存服務器 (redis)
  7. cookie (主流)
  8. 將信息存在加密後的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都是目前比較流行的微服務框架。


分享到:


相關文章: