0、介紹 本篇文章是在我看完《從零開始學架構》之後,以架構演變為主線,梳理了一下演變過程中出現的問題以及解決方案,文章中引用了這本書的一些內容和圖片 分散式和集群的概念經常被搞混,現在一句話讓你明白兩者的區別。 分散式:一個業務拆分成多個子業務,部署在不同的伺服器上集群:同一個業務,部署在多個伺服器 ...
0、介紹
本篇文章是在我看完《從零開始學架構》之後,以架構演變為主線,梳理了一下演變過程中出現的問題以及解決方案,文章中引用了這本書的一些內容和圖片
分散式和集群的概念經常被搞混,現在一句話讓你明白兩者的區別。
分散式:一個業務拆分成多個子業務,部署在不同的伺服器上
集群:同一個業務,部署在多個伺服器上
例如:電商系統可以拆分成商品,訂單,用戶等子系統。這就是分散式,而為了應對併發,同時部署好幾個用戶系統,這就是集群
1、單應用架構
2、應用伺服器和數據伺服器分離
單機負載越來越來,所以要將應用伺服器和資料庫伺服器分離
3、應用伺服器做集群
每個系統的處理能力是有限的,為了提高併發訪問量,需要對應用伺服器做集群
這時會涉及到兩個問題:
-
負載均衡
-
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跨域共用問題有如下幾種方式
-
session sticky
將請求都落到同一個伺服器上,如Nginx的url hash
-
session replication
session複製,每台伺服器都保存一份相同的session
-
session 集中存儲
存儲在db、 存儲在緩存伺服器 (redis)
-
cookie (主流)
將信息存在加密後的cookie中
4、資料庫讀寫分離
搭建資料庫主從集群,實現資料庫讀寫分離,改善資料庫負載壓力
資料庫讀寫分離的基本實現如下:
-
資料庫伺服器搭建主從集群,一主一從,一主多從都可以
-
資料庫主機負責讀寫操作,從機只負責讀操作
-
資料庫主機通過複製將數據同步到從機,每台資料庫伺服器都存儲了所有的業務數據
-
業務伺服器將寫操作分給資料庫主機,將讀操作分給資料庫從機
實現方式
讀寫分離需要將讀/寫操作區分開來,然後訪問不同的資料庫伺服器;分庫分表需要根據不同的數據訪問不同的資料庫伺服器,兩者本質上都是一種分配機制,即將不同的SQL語句發送到不同的資料庫伺服器。
讀寫分離,包括後面要提到的分庫分表的實現方式有兩種:
-
程式代碼封裝
-
中間件封裝
程式代碼封裝指在代碼中抽象一個數據訪問層來實現讀寫分離,分庫分表
中間件封裝指的是獨立一套系統出來,實現讀寫分離和分庫分表操作,如我們熟悉的MySQL Router和Mycat等
5、引入搜索引擎來查詢
傳統的關係型資料庫通過索引來達到快速查詢的目的,但是在全文搜索的業務場景下,索引也無能為力,主要體現在如下幾點:
-
全文搜索的條件可以隨意排列組合,如果通過索引來滿足,則索引的數量會非常多
-
全文搜索的模糊匹配方式,索引無法滿足,只能用like查詢,而like查詢是整表掃描,效率非常低
目前主要有Elasticsearch與Solr。Solr 是傳統搜索應用的有力解決方案,但 Elasticsearch 更適用於新興的實時搜索應用。
6、增加緩存
為了應對流量持續增加,必須增加緩存
常見的方式有如下幾種:
Redis與Memcached
以我們常見的Mybatis為例,很容易和Redis與Memcached整合起來,緩存已經查詢過的SQL,因為Mybatis知道自己不擅長緩存,所以提供了介面讓這些緩存工具進行整合
CDN
CDN是為瞭解決用戶網路訪問時的“最後一公裡”效應,本質上是一種“以空間換空間”的加速策略,即將內容緩存在離用戶最近的地方,用戶訪問的是緩存的內容,而不是站點實時的內容。
7、分庫分表
讀寫分離分散了資料庫讀寫操作的壓力,但沒有分散存儲壓力,當數據量達到千萬甚至上億條的時候,單台伺服器的存儲能力會成為系統的瓶頸。常見的分散存儲的方法有分庫和分表兩大類
業務分庫
業務分庫指的是按照業務模塊將數據分散到不同的資料庫伺服器。例如,一個簡單的電商網站,包括商品,訂單,用戶三個業務模塊,我們可以將商品數據,訂單數據,用戶數據,分開放到3台不同的資料庫伺服器上,而不是將所有數據都放在一臺資料庫伺服器上
當然業務分庫也會帶來新的問題:
-
join操作問題:業務分庫後,原本在同一個資料庫中的表分散到不同資料庫中,導致無法使用SQL的join查詢
-
事務問題:原本在同一個資料庫中不同的表可以在同一個事務中修改,業務分庫後,表分散到不同資料庫中,無法通過事務統一修改
-
成本問題:業務分庫同時也帶來了成本的代價,本來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都是目前比較流行的微服務框架。