高并发系统—高扩展
文章出自:阿里巴巴十億級并發系統設計(2021版)
鏈接:https://pan.baidu.com/s/1lbqQhDWjdZe1CBU-6U4jhA?提取碼:8888?
從架構設計上來說,高可擴展性是一個設計的指標,它表示可以通過增加機器的方式來線性提高系統的處理能力,從而承擔更高的流量和并發。
你可能會問:“在架構設計之初,為什么不預先考慮好使用多少臺機器,支持現有的并發 呢?”這個問題我在“03 | 系統設計目標(一):如何提升系統性能?”一課中提到過, 答案是峰值的流量不可控。一般來說,基于成本考慮,在業務平穩期,我們會預留 30%~50% 的冗余以應對運營活動或者推廣可能帶來的峰值流量,但是當有一個突發事件發生時,流量可能瞬間提升到 2~3 倍甚至更高,我們還是以微博為例。
鹿晗和關曉彤互圈公布戀情,大家會到兩個人的微博下面,或圍觀,或互動,微博的流量短時間內增長迅速,微博信息流也短暫出現無法刷出新的消息的情況。
那我們要如何應對突發的流量呢?架構的改造已經來不及了,最快的方式就是堆機器。不過 我們需要保證,擴容了三倍的機器之后,相應的我們的系統也能支撐三倍的流量。有的人可能會產生疑問:“這不是顯而易見的嗎?很簡單啊。”真的是這樣嗎?我們來看看做這件 事兒難在哪兒。
為什么提升擴展性會很復雜
在上一講中,我提到可以在單機系統中通過增加處理核心的方式,來增加系統的并行處理能力,但這個方式并不總生效。因為當并行的任務數較多時,系統會因為爭搶資源而達到性能 上的拐點,系統處理能力不升反降。
而對于由多臺機器組成的集群系統來說也是如此。集群系統中,不同的系統分層上可能存在一些“瓶頸點”,這些瓶頸點制約著系統的橫線擴展能力。這句話比較抽象,我舉個例子你就明白了。
比方說,你系統的流量是每秒 1000 次請求,對數據庫的請求量也是每秒 1000 次。如果流量增加 10 倍,雖然系統可以通過擴容正常服務,數據庫卻成了瓶頸。再比方說,單機網絡帶寬是 50Mbps,那么如果擴容到 30 臺機器,前端負載均衡的帶寬就超過了千兆帶寬的限制,也會成為瓶頸點。那么,我們的系統中存在哪些服務會成為制約系統擴展的重要因素呢?
其實,無狀態的服務和組件更易于擴展,而像 MySQL 這種存儲服務是有狀態的,就比較難以擴展。因為向存儲集群中增加或者減少機器時,會涉及大量數據的遷移,而一般傳統的關系型數據庫都不支持。這就是為什么提升系統擴展性會很復雜的主要原因。
除此之外,從例子中你可以看到,我們需要站在整體架構的角度,而不僅僅是業務服務器的角度來考慮系統的擴展性 。所以說,數據庫、緩存、依賴的第三方、負載均衡、交換機帶寬等等都是系統擴展時需要考慮的因素。我們要知道系統并發到了某一個量級之后,哪一個因素會成為我們的瓶頸點,從而針對性地進行擴展。
針對這些復雜的擴展性問題,我提煉了一些系統設計思路,供你了解。
高可擴展性的設計思路
拆分是提升系統擴展性最重要的一個思路,它會把龐雜的系統拆分成獨立的,有單一職責的模塊。相對于大系統來說,考慮一個一個小模塊的擴展性當然會簡單一些。將復雜的問題簡單化,這就是我們的思路。
但對于不同類型的模塊,我們在拆分上遵循的原則是不一樣的。我給你舉一個簡單的例子, 假如你要設計一個社區,那么社區會有幾個模塊呢?可能有 5 個模塊。
而部署方式遵照最簡單的三層部署架構,負載均衡負責請求的分發,應用服務器負責業務邏 輯的處理,數據庫負責數據的存儲落地。這時,所有模塊的業務代碼都混合在一起了,數據也都存儲在一個庫里。
1.?存儲層的擴展性
無論是存儲的數據量,還是并發訪問量,不同的業務模塊之間的量級相差很大,比如說成熟社區中,關系的數據量是遠遠大于用戶數據量的,但是用戶數據的訪問量卻遠比關系數據要大。所以假如存儲目前的瓶頸點是容量,那么我們只需要針對關系模塊的數據做拆分就好 了,而不需要拆分用戶模塊的數據。所以存儲拆分首先考慮的維度是業務維度。
拆分之后,這個簡單的社區系統就有了用戶庫、內容庫、評論庫、點贊庫和關系庫。這么做還能隔離故障,某一個庫“掛了”不會影響到其它的數據庫。
章節小插曲:本篇只總結了"高并發系統設計"的一部分內容,還有更多資源正在整理中包含?JVM、Tomcat、MySQL、Spring、Mybatis、Redis、MongoDB、Mycat、Zookeeper Nginx、RabbitMq、RocketMq、Kafka、Dubbo、Docker 分布式(架構設計,事務場景策略,場景解決方案,任務調度場景實現) 高并發場景應對方案、性能調優實戰、海量數據場景實現、消息服務總線MQ等。 不定期更新中獲取可添加助理QQ:【2986706524】?備注:【Java】審核通過后私信:【資料】?即可獲取。
按照業務拆分,在一定程度上提升了系統的擴展性,但系統運行時間長了之后,單一的業務數據庫在容量和并發請求量上仍然會超過單機的限制。這時,我們就需要針對數據庫做第二次拆分。
這次拆分是按照數據特征做水平的拆分,比如說我們可以給用戶庫增加兩個節點,然后按照某些算法將用戶的數據拆分到這三個庫里面,具體的算法我會在后面講述數據庫分庫分表時和你細說。 水平拆分之后,我們就可以讓數據庫突破單機的限制了。但這里要注意,我們不能隨意地增加節點,因為一旦增加節點就需要手動地遷移數據,成本還是很高的。所以基于長遠的考慮,我們最好一次性增加足夠的節點以避免頻繁地擴容。
當數據庫按照業務和數據維度拆分之后,我們盡量不要使用事務。因為當一個事務中同時更 新不同的數據庫時,需要使用二階段提交,來協調所有數據庫要么全部更新成功,要么全部 更新失敗。這個協調的成本會隨著資源的擴展不斷升高,最終達到無法承受的程度。
說完了存儲層的擴展性,我們來看看業務層是如何做到易于擴展的。
2.?業務層的擴展性
我們一般會從三個維度考慮業務層的拆分方案,它們分別是:業務緯度,重要性緯度和請求來源緯度。
首先,我們需要把相同業務的服務拆分成單獨的業務池,比方說上面的社區系統中,我們可 以按照業務的維度拆分成用戶池、內容池、關系池、評論池、點贊池和搜索池。
每個業務依賴獨自的數據庫資源,不會依賴其它業務的數據庫資源。這樣當某一個業務的接口成為瓶頸時,我們只需要擴展業務的池子,以及確認上下游的依賴方就可以了,這樣就大 大減少了擴容的復雜度。
除此之外,我們還可以根據業務接口的重要程度,把業務分為核心池和非核心池。打個比 方,就關系池而言,關注、取消關注接口相對重要一些,可以放在核心池里面;拉黑和取消 拉黑的操作就相對不那么重要,可以放在非核心池里面。這樣,我們可以優先保證核心池的 性能,當整體流量上升時優先擴容核心池,降級部分非核心池的接口,從而保證整體系統的 穩定性。
最后,你還可以根據接入客戶端類型的不同做業務池的拆分。比如說,服務于客戶端接口的
業務可以定義為外網池,服務于小程序或者 HTML5 頁面的業務可以定義為 H5 池,服務于 內部其它部門的業務可以定義為內網池,等等。
課程小結
本節課我帶你了解了提升系統擴展性的復雜度以及系統拆分的思路。拆分看起來比較簡單, 可是什么時候做拆分,如何做拆分還是有很多細節考慮的。
未做拆分的系統雖然可擴展性不強,但是卻足夠簡單,無論是系統開發還是運行維護都不需要投入很大的精力。拆分之后,需求開發需要橫跨多個系統多個小團隊,排查問題也需要涉 及多個系統,運行維護上,可能每個子系統都需要有專人來負責,對于團隊是一個比較大的 考驗。這個考驗是我們必須要經歷的一個大坎,需要我們做好準備。
總結
- 上一篇: 高并发系统—高可用
- 下一篇: API网关—系统的门面要如何做呢?