1. 架構(gòu)設(shè)計的目的
架構(gòu)設(shè)計的主要目的是為了解決軟件復(fù)雜度帶來的問題(復(fù)雜度又包括業(yè)務(wù)復(fù)雜度技術(shù)復(fù)雜度等)
2. 復(fù)雜度的來源
-
1. 高性能
高性能帶來的復(fù)雜度主要包括兩方面 ,一方面是單臺計算機(jī)內(nèi)部為了提高性能帶來的復(fù)雜度;另一方面是多臺計算機(jī)集群為了提高性能帶來的復(fù)雜度。
- 單機(jī)復(fù)雜度
計算機(jī)的復(fù)雜度關(guān)鍵是操作系統(tǒng),操作系統(tǒng)的復(fù)雜度直接決定了軟件系統(tǒng)的復(fù)雜度,而操作系統(tǒng)和性能最相關(guān)的就是進(jìn)程和線程。
多進(jìn)程讓人物能并行處理,但內(nèi)部只能穿行處理,所以又有了線程,有了線程又有了互斥鎖機(jī)制,操作系統(tǒng)的最小調(diào)度單位變成了線程,而進(jìn)程變成了操作系統(tǒng)分配資源的最小單位。
如果我們要設(shè)計一個高性能軟件,需要考慮多進(jìn)程、多線程、進(jìn)程通信、多線程并發(fā)等技術(shù),而這些技術(shù)不是最新最好。比如,Nginx可以用多線程也可以用多進(jìn)程,JBoss采用多線程;Redis采用多進(jìn)程,Memcache采用多線程,這些系統(tǒng)都實現(xiàn)了高性能,但內(nèi)部實現(xiàn)差異很大。
總而言之就是你要實現(xiàn)高性能必然要帶來復(fù)雜度,而我們要做的就是權(quán)衡好復(fù)雜度和性能
- 單機(jī)復(fù)雜度
-
- 集群復(fù)雜度
互聯(lián)網(wǎng)時代,單機(jī)支撐不了業(yè)務(wù),采用集群方式是必然的。
-
① 任務(wù)分配
image.png
任務(wù)分配器可以是F5、交換機(jī)、LVS,Nginx、HAProxy,還可以自己開發(fā)。并且任務(wù)分配器需要增加算法,所以我們明顯感覺到提升了復(fù)雜度
如果我們要繼續(xù)提高性能,任務(wù)分配器就不夠用了,架構(gòu)又會變成這個樣子
image.png
任務(wù)分配器1臺變成多臺,這個變化帶來的復(fù)雜度就是需要將不同的用戶分配到不同的分配器上常見的方法包括:DNS輪訓(xùn)、智能DNS、CDN(內(nèi)容分發(fā)網(wǎng)絡(luò))、GSLB(全局負(fù)載均衡)設(shè)備等 -
② 任務(wù)分解
任務(wù)分配可以突破單臺機(jī)器的性能瓶頸,但如果越來越復(fù)雜,單純通過任務(wù)分配的方式收益越來越低。例如10臺提升8倍,20臺10倍,為了提升性能我們使用任務(wù)分解
以微信后臺架構(gòu)為例
image.png
通過任務(wù)分解,把原來大一統(tǒng)但復(fù)雜的業(yè)務(wù)系統(tǒng)進(jìn)行拆分,但是不同任務(wù)拆分應(yīng)該控制一個合理的范圍不能無限拆分,因為拆分過多系統(tǒng)間調(diào)用次數(shù)會成指數(shù)遞增,而系統(tǒng)間通信都是通過網(wǎng)絡(luò),下圖進(jìn)行說明:
image.png
系統(tǒng)調(diào)用過多會導(dǎo)致網(wǎng)絡(luò)耗時過長,雖然計算時間縮短。
- 集群復(fù)雜度
-
2. 高可用
系統(tǒng)無中斷的地執(zhí)行功能的能力,代表系統(tǒng)的可用程度,是進(jìn)行系統(tǒng)設(shè)計時的準(zhǔn)則
系統(tǒng)的高可用方案五花八門,但萬變不離其宗,本質(zhì)都是通過冗余來實現(xiàn)高可用。通俗講就是增加機(jī)器,高性能增加機(jī)器目的在于“擴(kuò)展”性能;高可用增加機(jī)器目的在于“冗余”處理單元 。
- 計算高可用
這里的計算是指業(yè)務(wù)邏輯的處理,無論在哪臺機(jī)器預(yù)算輸出結(jié)果是一樣的。所以將計算從一臺機(jī)器遷移到另一臺機(jī)器對業(yè)務(wù)沒影響。先看一個單機(jī)變雙機(jī)
image.png
你可能發(fā)現(xiàn)和之前的“高性能”講到的雙機(jī)架構(gòu)圖是一樣的,因此復(fù)雜度也類似,具體表現(xiàn)為:增加人物分配器,分配器和業(yè)務(wù)機(jī)器之間連接和交互,人物分配器增加分配算法。再看一個高可用集群架構(gòu):
image.png
分配算法更加復(fù)雜可以是 1主3備、2主2備、3主1備、4主0備。例如ZooKeeper 是1主多備,Memcached是全主0備
- 計算高可用
- 存儲高可用
需要存儲數(shù)據(jù)的系統(tǒng),關(guān)鍵點和難點在于“存儲高可用”,存儲和計算相比本質(zhì)區(qū)別是:將數(shù)據(jù)從一臺機(jī)器搬到另一臺機(jī)器,需要經(jīng)過線路進(jìn)行傳輸,傳輸就有延時。按照數(shù)據(jù)+邏輯=業(yè)務(wù)來說,數(shù)據(jù)不一致邏輯一致業(yè)務(wù)最終表現(xiàn)也將不一樣。
比如在北京存了1萬塊,到上海查一下錢還沒到賬。
image.png
除了物理上傳輸速度限制,傳輸線路本身也存在高可用,傳輸線路可能中斷、擁塞、丟包,支付寶2015年就被挖斷過光纜影響超過4小時才恢復(fù)
綜合分析:數(shù)據(jù)不一致會導(dǎo)致業(yè)務(wù)問題,但如果不做冗余系統(tǒng)整體高可用無法得到保證,所以存儲高可用的難點不在于如何備份數(shù)據(jù),而在于如何減少或者規(guī)避數(shù)據(jù)不一致對業(yè)務(wù)的造成的影響。
- 存儲高可用
-
- 高可用狀態(tài)決策
無論計算高可用還是存儲高可用,其基礎(chǔ)都是“狀態(tài)決策”,就是系統(tǒng)需要判斷當(dāng)前的狀態(tài)是否異常,如果異常了采取行動保證高可用。(我理解就是要有人監(jiān)控系統(tǒng)是否不可用,不可用就趕緊切換到好用的機(jī)器上)
-
獨裁式
image.png
這種方式問題在于又要實現(xiàn)決策者的高可用,要加機(jī)器,導(dǎo)致進(jìn)入死循環(huán)。
-
- 協(xié)商式
是指兩個獨立的個體通過交流信息,然后根據(jù)規(guī)則進(jìn)行決策,最常用的協(xié)商式?jīng)Q策就是主備決策
image.png
問題在于兩個協(xié)商的服務(wù)器之間網(wǎng)絡(luò)可能出現(xiàn)波動,無法協(xié)商出正確的結(jié)果,比如主機(jī)沒事但是網(wǎng)絡(luò)不好備機(jī)以為主機(jī)掛了自己升級為主機(jī)此時就有兩個主機(jī)了。
image.png
連接不穩(wěn)定我們可以多加連接但是又會帶來問題,對多連接出來數(shù)據(jù)的取舍問題,反正又會增加復(fù)雜度。
image.png
綜合分析協(xié)商式在某些場景總是存在一些問題。
- 協(xié)商式
- 高可用狀態(tài)決策
-
民主式
是多個獨立的個體通過投票進(jìn)行狀態(tài)決策。例如ZooKeeper集群選舉leader就是采用這種方式,ZooKeeper的選舉算法Paxos。
image.png
-
-
3. 可擴(kuò)展
可擴(kuò)展性指系統(tǒng)為了應(yīng)對將來需求變化而提供的一種擴(kuò)展能力,當(dāng)有新需求時,系統(tǒng)不需要或者僅需要少量修改就可以支持,無須整個系統(tǒng)重構(gòu)或者重建。
-
4. 成本、安全、規(guī)模
顧名思義自行理解
3.架構(gòu)設(shè)計的原則
- 合適原則
合適優(yōu)于業(yè)界領(lǐng)先
- 合適原則
- 簡單原則
簡單優(yōu)于復(fù)雜
- 簡單原則
- 演化原則
演化優(yōu)于一步到位
- 演化原則
4. 架構(gòu)設(shè)計流程
-
1.識別復(fù)雜度
復(fù)雜度主要來源于“高性能”、“高可用”、“可擴(kuò)展”等幾個方面,但并不意味著我們的架構(gòu)必須要同時滿足這三個條件。
比如一個“億級用戶平臺”一開始就設(shè)計了40多個子系統(tǒng),上線之后發(fā)現(xiàn)過渡設(shè)計了,帶來諸多問題:
- 系統(tǒng)復(fù)雜無比
- 系統(tǒng)太多升級開發(fā)麻煩效率低
- 難以定位問題
此時正確的做法是將主要的復(fù)雜度列出來,然后根據(jù)業(yè)務(wù)、技術(shù)、團(tuán)隊等綜合情況進(jìn)行排序,優(yōu)先解決當(dāng)前面臨的主要復(fù)雜度問題,“億級用戶平臺”團(tuán)隊首先需要將系統(tǒng)的數(shù)量降下來 -
2.設(shè)計備選方案
-
3.評估和選擇備選方案
-
4.詳細(xì)方案設(shè)計
接下來逐一介紹“高性能架構(gòu)模式”,“高可用架構(gòu)模式”,“可擴(kuò)展架構(gòu)模式”
5.高性能數(shù)據(jù)庫集群
-
1. 讀寫分離
讀寫分離的基本原理是將數(shù)據(jù)庫讀寫操作分散到不同的節(jié)點上
基本架構(gòu)圖如圖:

讀寫分離的基本實現(xiàn):
- 數(shù)據(jù)庫搭建主從集群,一主一從、一主多從。
- 主機(jī)負(fù)責(zé)讀寫,從機(jī)只負(fù)責(zé)讀
- 主機(jī)通過復(fù)制將數(shù)據(jù)同步到從機(jī),每臺數(shù)據(jù)庫都存儲了所有的業(yè)務(wù)數(shù)據(jù)。
-
業(yè)務(wù)服務(wù)器將寫操作給主機(jī),讀操作給發(fā)給從機(jī)
這里說的是主從而不是主備,從是要干活的,而備不用只是用來存儲數(shù)據(jù)。
讀寫分離實現(xiàn)邏輯簡并不復(fù)雜,但是引入了設(shè)計復(fù)雜度:主從復(fù)制延遲和分配機(jī)制
復(fù)制延遲
主從復(fù)制延遲可能達(dá)到一秒,解決方式有:
- 寫操作后的讀操作發(fā)送給數(shù)據(jù)庫主服務(wù)器。例如注冊完之后登錄,要實現(xiàn)的話需要入侵代碼,在代碼里對所有的登錄指定查詢主服務(wù)器。
- 讀從機(jī)失敗再讀主機(jī),也就是所謂的“二次讀取”,但是這樣子會增大主機(jī)的讀壓力
- 關(guān)鍵業(yè)務(wù)讀寫指向主機(jī),非關(guān)鍵業(yè)務(wù)采用讀寫分離。
分配機(jī)制
-
程序代碼封裝
指在代碼中抽象一個數(shù)據(jù)訪問層,實現(xiàn)讀寫分離和數(shù)據(jù)庫連接的管理。例如基于。Hibernate進(jìn)行簡單封裝,就可以實現(xiàn)讀寫分離,基本架構(gòu)是:
image.png
特點:一是實現(xiàn)簡單,根據(jù)業(yè)務(wù)實現(xiàn)更多的定制化,二每個編程語言都要實現(xiàn)一次無法通用,三故障情況下主從切換配置需要改需要重啟服務(wù)。
目前的開源方案比如有淘寶的TDDL,它是一個通用的數(shù)據(jù)訪問層,所有功能封裝在jar包中供業(yè)務(wù)代碼調(diào)用 - 數(shù)據(jù)庫中間件
中間件封裝指的是獨立一套系統(tǒng)出來,實現(xiàn)讀寫分離操作和數(shù)據(jù)庫服務(wù)器訪問連接管理。中間件對服務(wù)器提供sql兼容的協(xié)議,對于服務(wù)器來說訪問中間件和訪問數(shù)據(jù)庫沒有區(qū)別,在業(yè)務(wù)服務(wù)器來說中間件就是一個數(shù)據(jù)庫服務(wù)器?;炯軜?gòu)如下:
image.png
特點是:- 支持多種編程語言。
- 實現(xiàn)復(fù)雜,細(xì)節(jié)多容易出現(xiàn)bug。
- 中間件不執(zhí)行讀寫但是讀寫都要經(jīng)過中間件,對中間件性能要求高。
- 數(shù)據(jù)庫主從切換對業(yè)務(wù)服務(wù)器無感知,中間件可以探測數(shù)據(jù)庫服務(wù)器的主從狀態(tài)。例如向一個測試表寫入數(shù)據(jù)成是主,失敗是從。
由于中間件比程序代碼封裝復(fù)雜度高出一個量級,一般情況下不建議采用。目前開源的數(shù)據(jù)庫中間件方案中有,MYSQL官方的MySQL Proxy 360 的Atlas.
-
2. 分庫分表
當(dāng)數(shù)據(jù)量上升到億級的時候,單臺數(shù)據(jù)庫的存儲能力就會成為瓶頸,主要體現(xiàn)在
- 數(shù)據(jù)量大,讀寫性能下降,即使有索引,索引性能一樣會下降。
- 數(shù)據(jù)文件很大,備份恢復(fù)需要時間邊長。
- 數(shù)據(jù)文件變大,極端情況下丟失數(shù)據(jù)風(fēng)險高
為了滿足數(shù)據(jù)存儲的需求,就需要將存儲分到到多臺機(jī)器上。
業(yè)務(wù)分庫
按照業(yè)務(wù)模塊將數(shù)據(jù)分散到不同的數(shù)據(jù)庫服務(wù)器。

但是同時帶來了問題
- join問題
業(yè)務(wù)分庫后,表分散在不同數(shù)據(jù)庫,導(dǎo)致無法使用sql的join查詢
- join問題
- 事務(wù)問題
原本在一個數(shù)據(jù)庫中不同表可以在同一個事務(wù)中修改,業(yè)務(wù)分庫后,分散在不同的數(shù)據(jù)中,無法通過事務(wù)同意修改。雖然數(shù)據(jù)廠商提供了分布式事務(wù)的解決方案(MySQL的XA),但性能太低,與高性能的目標(biāo)是違背的。
- 事務(wù)問題
- 成本問題
本來1臺搞定現(xiàn)在要3臺加上備份要6臺。
- 成本問題
分表
單表數(shù)據(jù)量變大需要分表,拆分方式有兩種:水平拆垂直拆。示意圖如下:

6.高性能NoSQL
常見的NoSQL的方案分為四類:
- K-V存儲:解決關(guān)系數(shù)據(jù)庫無法存儲數(shù)據(jù)結(jié)構(gòu)的問題,以Redis為代表
- 文檔數(shù)據(jù)庫: 解決關(guān)系數(shù)據(jù)庫schema約束的問題,以MongoDB為代表
- 列式數(shù)據(jù)庫: 解決關(guān)系數(shù)據(jù)庫大數(shù)據(jù)場景下的IO問題,以HBase為代表
- 全文搜索引擎: 解決關(guān)系數(shù)據(jù)庫的全文搜索性能問題,以Elasticsearch為代表
7.高性能緩存架構(gòu)
在一些場景下,單純依靠存儲系統(tǒng)的性能提升不夠,典型場景如下:
- 需要經(jīng)過復(fù)雜運算后得出結(jié)果,存儲系統(tǒng)無能為力
-
讀多寫少的數(shù)據(jù),存儲系統(tǒng)有心無力
緩存基本原理就是將重復(fù)使用的數(shù)據(jù)放到內(nèi)存中,一次生成多次使用,避免每次都訪問存儲系統(tǒng)。
以Memcache為例單臺服務(wù)器簡單的key-value查詢能夠達(dá)到TPS 50000以上加入如下:
image.png
緩存雖然能夠大大減輕存儲系統(tǒng)壓力,但是同時也給架構(gòu)帶來了更多的復(fù)雜性,架構(gòu)如果沒有針對復(fù)雜性進(jìn)行處理,某些場景下會導(dǎo)致整個系統(tǒng)崩潰。
緩存穿透
緩存穿透是指緩存沒有發(fā)揮作用,業(yè)務(wù)系統(tǒng)雖然去緩存查詢數(shù)據(jù)但是緩存中沒有數(shù)據(jù),業(yè)務(wù)系統(tǒng)需要再次到存儲系統(tǒng)查詢數(shù)據(jù)。通常有兩種情況:
- 1.存儲數(shù)據(jù)不存在
存儲系統(tǒng)確實不存在數(shù)據(jù),所以緩存也沒有數(shù)據(jù),導(dǎo)致每次查詢都還是查詢存儲系統(tǒng)。通常情況下業(yè)務(wù)上出現(xiàn)數(shù)據(jù)不存在的問題并不常見,但是出現(xiàn)了容易被黑客攻擊,解決辦法如果存儲系統(tǒng)數(shù)據(jù)不存在,就放一個默認(rèn)值到緩存中。 - 緩存數(shù)據(jù)生成耗費大量時間或資源。
典型場景就是電商商品分頁,不能把所有商品都緩存起來,只能按照分頁緩存。通常用戶也不會看到最后那些頁,但是如果被爬蟲爬取就沒有辦法了。
- 緩存數(shù)據(jù)生成耗費大量時間或資源。
緩存雪崩
指的是大量緩存集中在一段時間內(nèi)失效,發(fā)生大量的緩存穿透,所有的查詢都落在數(shù)據(jù)庫上,造成了緩存雪崩。
緩存雪崩常見解決方式有兩種:更新鎖機(jī)制和后臺更新機(jī)制。
- 更新鎖
對緩存更新操作進(jìn)行加鎖保護(hù),保證只有一個線程能夠進(jìn)行緩存更新,未能獲取更新鎖的線程要么等待鎖釋放然后讀取緩存,要么直接返回空值。對于分布式業(yè)務(wù)系統(tǒng),由于存在多臺服務(wù)器,單臺服務(wù)器有一個線程更新緩存,多臺就有多個線程更新緩存,因此要實現(xiàn)鎖機(jī)制,需要用到分布式鎖,如ZooKeeper。- 緩存大面積失效
- 線程1 讀取緩存,判斷緩存為空,獲得更新鎖,從數(shù)據(jù)庫讀取,寫入緩存
- 同時線程2 讀取緩存,判斷緩存為空,無法獲得更新鎖,等待鎖釋放重新讀取緩存,也可以返回默認(rèn)值
- 線程1 未釋放鎖,將緩存放入數(shù)據(jù)之前所有的線程都無法查詢數(shù)據(jù)庫
- 更新鎖
- 后臺更新
由后臺線程更新,而不是由業(yè)務(wù)線程來更新,緩存本身有效期設(shè)置為永久,后臺線程定時更新緩存
- 后臺更新
緩存熱點
緩存中的某些Key(可能對應(yīng)用與某個促銷商品)對應(yīng)的value存儲在集群中一臺機(jī)器,使得所有流量涌向同一機(jī)器,成為系統(tǒng)的瓶頸,該問題的挑戰(zhàn)在于它無法通過增加機(jī)器容量來解決。
緩存熱點的解決方案就是復(fù)制多份緩存副本,將請求分散到多個緩存服務(wù)器上,減輕緩存熱點導(dǎo)致的單臺緩存服務(wù)器壓力。
將熱點key復(fù)制多個副本,然后存儲到緩存集群的不同機(jī)器上。當(dāng)通過熱點key去查詢數(shù)據(jù)時,通過某種hash算法隨機(jī)選擇一個副本機(jī)器訪問緩存,將熱點分散到了不同機(jī)器上。
以微博為例,對于粉絲超過100萬的明星,每條微博生成100萬份緩存,緩存的數(shù)據(jù)是一樣的,通過在緩存的key里加上編號進(jìn)行區(qū)分,每次讀取緩存的時候都隨機(jī)讀取其中的某份緩存。
緩存副本設(shè)計有一個細(xì)節(jié),就是不同的緩存副本不要設(shè)置統(tǒng)一的過期時間,否則就會出現(xiàn)緩存副本同時失效的情況。
8.單服務(wù)器高性能模式
高性能架構(gòu)主要集中在兩個方面:
1. 盡量提升單服務(wù)器的性能,將單服務(wù)器的性能發(fā)揮到極致。
2. 如果單服務(wù)器無法支撐,設(shè)置服務(wù)器集群方案。
單服務(wù)器高性能的關(guān)鍵之一就是服務(wù)器采取的并發(fā)模型,并發(fā)模型設(shè)計有如下兩個關(guān)鍵設(shè)計點
- 服務(wù)器如何管理連接
-
服務(wù)器如何處理請求
以上兩個設(shè)計點最終都和操作系統(tǒng)的I/O模型及進(jìn)程模型相關(guān)。 - I/O模型:阻塞、非阻塞、同步、異步
- 進(jìn)程模型: 單進(jìn)程、多進(jìn)程、多線程
但服務(wù)器高性能模式:PPC與TPC
PPC(Process Per Connction)
PPC其含義是指每次有新的連接就新建一個進(jìn)程專門處理這個請求,這是傳統(tǒng)的UNIX網(wǎng)絡(luò)服務(wù)器所采用的模型。
基本流程圖是:

- 父進(jìn)程“fork”子進(jìn)程(途中fork)。
- 子進(jìn)程處理連接的讀寫請求(途中read,業(yè)務(wù)處理,writee)。
- 子進(jìn)程關(guān)閉連接(圖中子進(jìn)程的close)
圖中父進(jìn)程“fork"子進(jìn)程后,直接調(diào)用了close,看起來是關(guān)閉了父進(jìn)程,其實只是將連接的文件的描述引用減一,真正關(guān)閉連接是等子進(jìn)程也調(diào)用close后,連接對應(yīng)的文件描述引用計數(shù)變?yōu)?后,操作系統(tǒng)才會真正關(guān)閉,更多細(xì)節(jié)參考《UNIX網(wǎng)絡(luò)編程: 卷一》(mark一下記得學(xué)習(xí))。
PPC模式適合服務(wù)器連接沒那么多的情況,例如數(shù)據(jù)庫服務(wù)器。因為fork代價高,父子進(jìn)程通訊復(fù)雜,支持并發(fā)連接數(shù)有限。
prefork
PPC模式,當(dāng)連接進(jìn)來的時候才fork新進(jìn)程,由于fork進(jìn)程代價太高,用戶訪問就會比較慢
prefork提前創(chuàng)建進(jìn)程,系統(tǒng)在啟動的時候預(yù)先創(chuàng)建進(jìn)程,然后才開始接受用戶的請求,當(dāng)新進(jìn)程進(jìn)來的時候,就可以省去fork進(jìn)程的操作,基本示意圖:

prefork的實現(xiàn)關(guān)鍵就是多個子進(jìn)程都accept同一個socket,當(dāng)有新的連接進(jìn)入時,操作系統(tǒng)保證只有一個進(jìn)程能夠accept成功。但存在”驚群”現(xiàn)象,雖然只有一個進(jìn)程accept成功,但所有阻塞在accept上的進(jìn)程都會被喚醒,這樣就導(dǎo)致了不必要的進(jìn)程調(diào)度和上下文切換。幸運的是Linux2.6已經(jīng)解決了accept驚群問題。
prfork和ppc模式一樣存在父子進(jìn)程通訊復(fù)雜問題。
TPC(Thread Per Connection)
新連接就建線程處理。更輕量級,創(chuàng)建線程的消耗比進(jìn)程要少得多;同時多線程是共享進(jìn)程內(nèi)存空間的,線程通信相比進(jìn)程通信更簡單。因此,TPC 實際上是解決或者弱化了 PPC fork 代價高的問題和父子進(jìn)程通信復(fù)雜的問題。
TPC基本流程

父進(jìn)程接受連接(圖中 accept)。
父進(jìn)程創(chuàng)建子線程(圖中 pthread)。
子線程處理連接的讀寫請求(圖中子線程 read、業(yè)務(wù)處理、write)。
子線程關(guān)閉連接(圖中子線程中的 close)。
和PPC相比,主進(jìn)程不用“close”連接,原因是在于子線程是共享主進(jìn)程的進(jìn)程空間的,連接的文件描述符并沒有被復(fù)制,因此只需要一次 close 即可。
TPC 雖然解決了 fork 代價高和進(jìn)程通信復(fù)雜的問題,但是也引入了新的問題,具體表現(xiàn)在:
創(chuàng)建線程雖然比創(chuàng)建進(jìn)程代價低,但并不是沒有代價,高并發(fā)時(例如每秒上萬連接)還是有性能問題。
無須進(jìn)程間通信,但是線程間的互斥和共享又引入了復(fù)雜度,可能一不小心就導(dǎo)致了死鎖問題。
多線程會出現(xiàn)互相影響的情況,某個線程出現(xiàn)異常時,可能導(dǎo)致整個進(jìn)程退出(例如內(nèi)存越界)。
除了引入了新的問題,TPC 還是存在 CPU 線程調(diào)度和切換代價的問題。因此,TPC 方案本質(zhì)上和 PPC 方案基本類似,在并發(fā)幾百連接的場景下,反而更多地是采用 PPC 的方案,因為 PPC 方案不會有死鎖的風(fēng)險,也不會多進(jìn)程互相影響,穩(wěn)定性更高。
prethread
TPC模式中,當(dāng)連接進(jìn)來的時候才創(chuàng)建線程來處理請求,雖然創(chuàng)建線程比創(chuàng)建進(jìn)程更加輕量級,但是又一定代價,而prethread就是為了解決這個問題。
和prefork模式相似,prethread模式會預(yù)先創(chuàng)建線程,然后才開始接受用戶請求。由于多線程之間數(shù)據(jù)共享和通信比較方便,因此實際上 prethread 的實現(xiàn)方式相比 prefork 要靈活一些,常見的實現(xiàn)方式有下面幾種:
主進(jìn)程 accept,然后將連接交給某個線程處理。
-
子線程都嘗試去 accept,最終只有一個線程 accept 成功,方案的基本示意圖如下:
image.png
Apache 服務(wù)器的 MPM worker 模式本質(zhì)上就是一種 prethread 方案,但稍微做了改進(jìn)。Apache 服務(wù)器會首先創(chuàng)建多個進(jìn)程,每個進(jìn)程里面再創(chuàng)建多個線程,這樣做主要是為了考慮穩(wěn)定性,即:即使某個子進(jìn)程里面的某個線程異常導(dǎo)致整個子進(jìn)程退出,還會有其他子進(jìn)程繼續(xù)提供服務(wù),不會導(dǎo)致整個服務(wù)器全部掛掉。
prethread 理論上可以比 prefork 支持更多的并發(fā)連接,Apache 服務(wù)器 MPM worker 模式默認(rèn)支持 16 × 25 = 400 個并發(fā)處理線程。
Reactor
(有時間再學(xué)習(xí))
Proactor
(有時間再學(xué)習(xí))
9.高性能負(fù)載均衡
負(fù)載均衡分為三類:DNS負(fù)載均衡、硬件負(fù)載均衡和軟件負(fù)載均衡
DNS負(fù)載均衡
DNS是最簡單的負(fù)載均衡,一般用來實現(xiàn)地理級別的負(fù)載均衡。比如北方的用戶訪問北京的機(jī)房,南方的用戶訪問深圳的機(jī)房,下面是DNS負(fù)載均衡示意圖:

DNS負(fù)載均衡存在的缺點:
- 更新不及時
- 擴(kuò)展性差,控制權(quán)在域名商那里
- 分配策略簡單
硬件負(fù)載均衡
目前典型硬件負(fù)載均衡設(shè)備F5和A10。這類設(shè)備性能強(qiáng)大,但是比較貴??梢钥棺“偃f并發(fā)。
軟件負(fù)載均衡
軟件負(fù)載均衡有Nginx和LVS,Nginx是7層(應(yīng)用層)負(fù)載均衡,LVS是4層(傳輸層)負(fù)載均衡。
一般Nginx大概能達(dá)到5萬/秒的并發(fā);LVS性能是十萬級。下面是Nginx架構(gòu)示意圖:

如果實現(xiàn)Nginx 高可用就需要搭建Nginx集群,通過Keepalive監(jiān)聽Nginx服務(wù)器的狀態(tài),然后虛擬出一個對外的ip,誰掛了然后通過ip漂移將虛擬ip重新指向另一臺Nginx
負(fù)載均衡典型架構(gòu)

算法
- 輪詢
- 加權(quán)輪詢
- 負(fù)載最低優(yōu)先
- 性能最優(yōu)優(yōu)先
- Hash
- 源地址Hash
- ID Hash
CAP(Consistency、Availability、Partition Tolerance)
CAP理論:對于一個分布式計算系統(tǒng),不可能同時滿足一致性(Consistency)、可用性(Availability)、分區(qū)容錯性(Partition Tolerance)
-
1.一致性
對某個指定的客戶端來說,讀操作保證能返回最新寫操作的結(jié)果 -
2.可用性
非故障節(jié)點在合理的時間返回合理的響應(yīng)(不是錯誤和超時的響應(yīng)) -
3.分區(qū)容錯性
當(dāng)系統(tǒng)出現(xiàn)分區(qū)的時候,系統(tǒng)能繼續(xù)履行職責(zé)
10.CAP應(yīng)用
雖然CAP理論定義三個要素只能取兩個,但放到分布式環(huán)境中我們必須選擇P要素,因為網(wǎng)絡(luò)本身無法做到100%可靠,有可能出故障,所以分區(qū)是一個必然現(xiàn)象。如果我們選擇了CA而放棄了P,那么當(dāng)發(fā)生分區(qū)現(xiàn)象時,為了保證C系統(tǒng)要禁止寫入,當(dāng)有寫入時系統(tǒng)返回error(例如當(dāng)前系統(tǒng)禁止寫入),這又和A沖突了,因為A要返回no error 和no timeout 。因此分布式系統(tǒng)架構(gòu)理論上不能選擇CA,只能選擇CP或者AP。
CP
如下圖所示,為了保證一致性,當(dāng)發(fā)生分區(qū)現(xiàn)象后,N1的數(shù)據(jù)已經(jīng)更新到y(tǒng),但是由于N1和N2的通信中斷,數(shù)據(jù)無法實現(xiàn)復(fù)制。N2上的數(shù)據(jù)還是x,這時候要想要實現(xiàn)一致性。那么N2查詢返回的數(shù)據(jù)不能是x,不然和N1的返回就不一致了,所以N2只能返回Error,但是這樣子就違背了可用性。

AP
如下圖所示,當(dāng)發(fā)生分區(qū)的時候,N1的數(shù)據(jù)已經(jīng)更新到y(tǒng),但是N2的數(shù)據(jù)還是x。當(dāng)C訪問N2的時候,將數(shù)據(jù)x返回給C了。而實際當(dāng)前數(shù)據(jù)已經(jīng)更新為y了,這就不滿足一致性了,因此CAP只能滿足AP.
注意:這里N2返回的x不是一個正確的值,但是是一個合理的值,只是不是最新的數(shù)據(jù)而已。

CAP關(guān)鍵細(xì)節(jié)點
并不是整個系統(tǒng)要么選擇CP要么選擇AP。在實際的設(shè)計過程中,每個系統(tǒng)不可能只處理一種數(shù)據(jù),而包含多種類型的數(shù)據(jù),有的數(shù)據(jù)必須選擇CP,有的數(shù)據(jù)必須選擇AP,而如果們做設(shè)計時從整個系統(tǒng)的角度去選擇CP或者AP,就會顧此失彼。
舉個例子:
用戶管理系統(tǒng)包括用戶賬號數(shù)據(jù)、用戶信息數(shù)據(jù)。通常賬號數(shù)據(jù)會選擇CP,用戶信息數(shù)據(jù)會選擇AP,而如果限定整個系統(tǒng)為CP,則不符合用戶信息數(shù)據(jù)的應(yīng)用場景;
在CAP理論落地實踐時,我們需要將系統(tǒng)內(nèi)部數(shù)據(jù)按照不同的應(yīng)用場景進(jìn)行分類,每類數(shù)據(jù)選擇不同的策略,而不是限制整個系統(tǒng)數(shù)據(jù)都是用同一策略。
-
CAP是忽略網(wǎng)絡(luò)延遲的
當(dāng)事務(wù)提交時,數(shù)據(jù)并不能夠瞬間復(fù)制到所有節(jié)點。例如北京機(jī)房同步到廣州機(jī)房可能十幾毫秒,這就意味著CAP中的C是不能完美實現(xiàn)的,在復(fù)制的過程中,節(jié)點A和節(jié)點B數(shù)據(jù)并不一致。
不要小看這十幾毫秒,對于嚴(yán)苛的業(yè)務(wù)場景,例如和金錢相關(guān)的用戶余額,或者和搶購相關(guān)商品庫存,技術(shù)上是無法做到分布式場景下的一致性的。而業(yè)務(wù)上要求必須要實現(xiàn)一致性,因此單個用戶的余額、單個商品的庫存理論上要求選擇CP但是CP都做不到,只能選擇CA。也就是說只能單點寫入,其他節(jié)點做備份,無法做到分布式情況下多點寫入。
需要注意的是,這并不意味著系統(tǒng)無法應(yīng)用分布式架構(gòu),只是說“單個用戶余額、單個商品庫存”無法做分布式,單系統(tǒng)整體還是可以用分布式架構(gòu)的。例如下面將用戶分區(qū)的架構(gòu):
image.png
對于單個用戶來說,讀寫操作只能在某個節(jié)點上運行,對于所有用戶來說一部分用戶在Node1上一部分用戶在Node2上。當(dāng)一個節(jié)點出現(xiàn)故障的時候也只是部分用戶受到影響,這也就是為什么支付寶被挖斷光纜,部分用戶出現(xiàn)故障的原因。 -
正常運行情況下,不存在CP和AP的選擇,可以同時滿足CA
CAP理論告訴我們分布式系統(tǒng)只能選擇CP或者CP,但這里的前提是發(fā)生了分區(qū)現(xiàn)象,如果沒有發(fā)生分區(qū),也就是說P不存在的時候(節(jié)點網(wǎng)絡(luò)連接一切正常)。我們沒有必要放棄C或者A,應(yīng)該C和A都可以保證,這就要求架構(gòu)設(shè)計的時候既要考慮分區(qū)發(fā)生的時候CP還是AP,也要考慮分區(qū)沒有發(fā)生如何保證CA。 -
放棄并不等于什么都不做,需要為分區(qū)恢復(fù)做準(zhǔn)備
分區(qū)期間放棄C和A,并不意味著永遠(yuǎn)放棄,我們可以在分區(qū)期間進(jìn)行一些操作。從而分區(qū)故障解除后,系統(tǒng)能重新達(dá)到CA狀態(tài)。
最典型的就是在分區(qū)期間記錄一些日志,當(dāng)出現(xiàn)分區(qū)故障后,系統(tǒng)根據(jù)日志進(jìn)行數(shù)據(jù)恢復(fù),使得重新達(dá)到CA狀態(tài)。
BASE
BASE 是指基本可用(Basically Available)、軟狀態(tài)(Soft State)、最終一致性(Eventual Consistency),核心思想是即使無法做到強(qiáng)一致性(CAP的一致性就是強(qiáng)一致性),但應(yīng)用可以采用適合的方式達(dá)到最終一致性。
- 基本可用
分布式系統(tǒng)出現(xiàn)故障時,允許損失部分可用性,即保證核心可用 - 軟裝態(tài)
允許系統(tǒng)存在中間狀態(tài),而中間狀態(tài)不會影響系統(tǒng)的整體可用性。這里的中間狀態(tài)就是CAP理論中的數(shù)據(jù)不一致 - 最終一致性
系統(tǒng)中的所有數(shù)據(jù)副本經(jīng)過一定時間后,最終能夠達(dá)到一致狀態(tài)。
BASE理論是對CAP的延伸和補(bǔ)充 - CAP理論是忽略延時的,而實際應(yīng)用中延時是無法避免的。
這點意味著CP場景是不存在的,即使是幾毫秒的復(fù)制延遲,在這幾毫秒的時間內(nèi)系統(tǒng)是不符合CP要求的。因此CAP中的CP方案,也是實現(xiàn)了最終一致性,只是一定時間是幾毫秒而已 - AP方案犧牲一致性是指分區(qū)期間,而不是永遠(yuǎn)放棄一致性。
這其實是BASE理論延伸的地方,分區(qū)期間犧牲一致性,單分區(qū)故障結(jié)束后系統(tǒng)應(yīng)該恢復(fù)最終一致性。
綜上分析ACID是數(shù)據(jù)庫事務(wù)完整性理論,CAP是分布式系統(tǒng)設(shè)計理論,BASE是CAP理論中AP方案的延伸
11.排除架構(gòu)可用性隱患的利器FMEA方法
FMEA介紹
FMEA(Failure mode and effects analysis,故障模式與影響分析)

12.高可用存儲架構(gòu)
常用的存儲架構(gòu)有主備、主從、主主、集群、分區(qū)
雙機(jī)架構(gòu)
主備復(fù)制
主備復(fù)制是最常見也是最簡單的存儲高可用方案,幾乎所有存儲系統(tǒng)都提供了主備方案,例如MySQL、Redis、MongoDB等。下面是標(biāo)準(zhǔn)主備方案結(jié)構(gòu)圖:

備機(jī)主要是備份作用,并不承擔(dān)實際的業(yè)務(wù)讀寫,如果需要改備機(jī)為主機(jī)需要人工操作
主從復(fù)制
下面是標(biāo)準(zhǔn)的主從架構(gòu)圖:

雙機(jī)切換
主從和主備存在兩個共性問題:
- 主機(jī)故障后,無法進(jìn)行寫操作
- 如果主機(jī)無法恢復(fù),需要人工指定主機(jī)角色
雙機(jī)切換就是為了解決這兩個問題而產(chǎn)生的,包括主備切換和主從切換。
要實現(xiàn)完善的切換方案,必須考慮這幾個關(guān)鍵點:
- 主備狀態(tài)判斷
- 切換決策
- 切換時機(jī):什么情況下升級為主機(jī)
- 切換策略:原來主機(jī)變?yōu)閭錂C(jī)還是,故障好了仍然作為主機(jī)
- 自動程度:是自動還是人工
- 數(shù)據(jù)沖突解決
常見架構(gòu)
-
互連式
顧名思義,主備機(jī)直接建立狀態(tài)傳遞的渠道
image.png
可以看到在主備復(fù)制的架構(gòu)基礎(chǔ)上,主機(jī)和備機(jī)多了一個狀態(tài)傳遞的通道,這個通道就是來傳遞狀態(tài)信息的。
-
中介式
中介式指的是在主備兩者之外引入第三方中介,主備之間不直接連接,而都去連接中介,并且通過中介來傳遞狀態(tài)信息,其架構(gòu)圖如下:
image.png
但是有個一個缺點就是中介如何實現(xiàn)自己的高可用。MongoDB的Replia Set采取的就是這種方式,其架構(gòu)如下:
image.png
MondoDB(M)表示主節(jié)點,MongoDB(S)表示備節(jié)點,MongoDB(A)表示仲裁節(jié)點。主備節(jié)點存儲數(shù)據(jù),仲裁節(jié)點不存儲。幸運的是開源方案已經(jīng)有比較成熟的解決方案,例如Zookeeper和Keepalived。 -
模擬式
模擬式是指主備之間不傳遞任何狀態(tài)數(shù)據(jù),而是備機(jī)模擬成一個客戶端,向主機(jī)發(fā)起模擬的讀寫操作,根據(jù)讀寫操作的響應(yīng)情況來判斷主機(jī)的狀態(tài),其架構(gòu)圖如下:
image.png
主主復(fù)制
主主復(fù)制指兩臺機(jī)器都是主機(jī),互相將數(shù)據(jù)復(fù)制給對方,客戶端可以任意挑選一臺機(jī)器進(jìn)行讀寫操作,架構(gòu)圖如下:

如果采用主主復(fù)制必須保證數(shù)據(jù)能夠雙向復(fù)制,而很多數(shù)據(jù)是不能雙向復(fù)制的
集群和分區(qū)
數(shù)據(jù)集群
主從、主備、主主都是以主機(jī)能夠存儲所有數(shù)據(jù)為前提的。但是單臺機(jī)器明顯不夠用了,所以需要使用集群。集群可以劃分為兩類:數(shù)據(jù)集中集群、數(shù)據(jù)分散集群。
-
數(shù)據(jù)集中集群
數(shù)據(jù)集中集群與主從、主備這類架構(gòu)類似。下面是讀寫全到主機(jī)的一種架構(gòu):
image.png - 數(shù)據(jù)分散集群
數(shù)據(jù)分散集群指多個服務(wù)器組成一個集群,每個服務(wù)器都會存儲一部分?jǐn)?shù)據(jù);同時為了提升硬件利用率,每臺服務(wù)器又會備份一部分?jǐn)?shù)據(jù)。算法需要考慮這些設(shè)計點:- 均衡性
算法需要保證服務(wù)器上數(shù)據(jù)分布是均勻的。 - 容錯性
當(dāng)部分服務(wù)器故障時,算法需要將原來的分配給故障服務(wù)器的數(shù)據(jù)分區(qū)分配給其他服務(wù)器。 - 可伸縮性
當(dāng)容器容量不夠時,擴(kuò)充新的服務(wù)器后,算法能夠自動將部分?jǐn)?shù)據(jù)分區(qū)遷移到新的服務(wù)器,并保證擴(kuò)容后所有服務(wù)器的均衡性。
- 均衡性
數(shù)據(jù)分散集群中每臺機(jī)器都可以處理讀寫請求。但在數(shù)據(jù)分散集群中,必須有一個角色負(fù)責(zé)執(zhí)行數(shù)據(jù)分配算法,這個角色可以是獨立的服務(wù)器也可以是集群選舉出的服務(wù)器。
一般來說數(shù)據(jù)集中集群適合數(shù)據(jù)量不大,集群機(jī)器數(shù)量不多的場景。例如,Zookeeper集群,一般推薦5臺機(jī)器左右,數(shù)據(jù)量是單臺機(jī)器就能支撐的。而數(shù)據(jù)分散機(jī)群,由于其良好的伸縮性,適合業(yè)務(wù)數(shù)據(jù)巨大、機(jī)器數(shù)量龐大的業(yè)務(wù)場景。例如,Hadoop集群、HBase集群,大規(guī)模的集群可以達(dá)到上百甚至上千臺服務(wù)器
數(shù)據(jù)分區(qū)
前面討論的都是基于硬件故障場景去考慮和設(shè)計的,但對于影響非常大的災(zāi)難或者事故來說,有可能所有硬件全部故障。例如,奧爾良水災(zāi)、洛杉磯大地震等,這種情況下基于硬件設(shè)計的高可用架構(gòu)不再適用,這就是數(shù)據(jù)分區(qū)產(chǎn)生的背景。
數(shù)據(jù)分區(qū)指將數(shù)據(jù)按照一定規(guī)則進(jìn)行分區(qū),不同分區(qū)分布在不同的地理位置上,每個分區(qū)存儲一部分?jǐn)?shù)據(jù),通過這種方式來規(guī)避地理級別的故障造成的巨大影響。當(dāng)故障恢復(fù)后,其他地區(qū)備份的數(shù)據(jù)也可以幫助故障地區(qū)快速恢復(fù)業(yè)務(wù)。
設(shè)計一個好的分區(qū)架構(gòu)需要考慮多方面。
-
數(shù)據(jù)量
數(shù)據(jù)量大小直接決定了分區(qū)規(guī)則的復(fù)雜度 -
分區(qū)規(guī)則
地理位置有近有遠(yuǎn),因此可以得到不同的分區(qū)規(guī)則,包括洲際分區(qū)、國家分區(qū)、城市分區(qū)。 -
復(fù)制規(guī)則
每個分區(qū)雖然只是整體數(shù)據(jù)的一部分,但還是很大,這部分?jǐn)?shù)據(jù)如果丟失或損壞,同樣難以接受。因此使用分區(qū)架構(gòu),同樣考慮復(fù)制方案。常見的復(fù)制規(guī)則有:集中式、互備式和獨立式-
集中式
集中式備份指存在一個總的備份中心所有分區(qū)都將數(shù)據(jù)備份到備份中心,架構(gòu)如下:
image.png
但是成本高需要建一個備份中心
-
互備式
互備式指每個分區(qū)備份另外一個分區(qū)的數(shù)據(jù),其基本架構(gòu)如下:
image.png -
獨立式
獨立式每個分區(qū)有自己的備份中心,其基本架構(gòu)如下:
image.png
數(shù)據(jù)分區(qū)并不涉及多個分區(qū)之間業(yè)務(wù)數(shù)據(jù)的調(diào)用,而數(shù)據(jù)分散集群中各個服務(wù)器之間是要相互調(diào)用的。分散集群在地里位置上是相同的,分區(qū)分布在不同的地理位置。
-
13 .高可用計算架構(gòu)
計算高可用架構(gòu)的設(shè)計思想很簡單:通過增加更多服務(wù)器達(dá)到計算高可用
計算高可用架構(gòu)設(shè)計復(fù)雜度主要體現(xiàn)在任務(wù)管理方面,即當(dāng)任務(wù)在某臺服務(wù)器上執(zhí)行失敗后,如何將任務(wù)重新分配到新的服務(wù)器進(jìn)行執(zhí)行。計算高可用設(shè)計的關(guān)鍵點有下面兩點:
- 1.哪些服務(wù)器可以執(zhí)行任務(wù)
第一種方式和計算高性能中的集群類似,每個服務(wù)器都可以執(zhí)行任務(wù)。
第二種方式和存儲高可用中的集群類似,只有特定服務(wù)器(通常叫“主機(jī)”)可以執(zhí)行任務(wù)。 - 2.任務(wù)如何重新執(zhí)行
第一種策略對于已經(jīng)分配的任務(wù)即使執(zhí)行失敗也不做任何處理
第二種策略設(shè)計一個任務(wù)管理器來管理需要執(zhí)行的計算任務(wù),服務(wù)器執(zhí)行完后,需要將執(zhí)行結(jié)果反饋給任務(wù)管理器,管理器根據(jù)執(zhí)行結(jié)果決定是否將執(zhí)行任務(wù)重新分配到另外的服務(wù)器上執(zhí)行?!叭蝿?wù)分配器”是一個邏輯概念,并不一定要求系統(tǒng)存在一個獨立的任務(wù)分配模塊
接下來我們闡述常見的高可用架構(gòu):主備、主從和集群
主備
主備架構(gòu)是計算高可用最簡單的架構(gòu),和存儲高可用架構(gòu)類似,但更簡單些

根據(jù)備機(jī)的狀態(tài)不同又分為冷備架構(gòu)和溫備架構(gòu)
- 冷備:備機(jī)上程序包配置文件備好,但是業(yè)務(wù)系統(tǒng)沒有啟動。
- 溫備: 備機(jī)上業(yè)務(wù)系統(tǒng)已經(jīng)啟動但是不對外提供服務(wù)。
主從
和存儲高可用的主從架構(gòu)類似,計算高可用的從機(jī)也要執(zhí)行任務(wù)的。任務(wù)分配器需要將任務(wù)進(jìn)行分類,確定哪些任務(wù)發(fā)給主機(jī)哪些任務(wù)發(fā)給備機(jī)。架構(gòu)如下:

集群
主備主從通過冗余一臺服務(wù)器來提升性能,且需要人工來切換。高可用集群根據(jù)集群中服務(wù)器節(jié)點角色不同分為兩類:一類是對稱集群,一類是非對稱集群
計算高可用包含兩臺服務(wù)器的集群和存儲高可用不一樣。
-
對稱集群
對稱集群通俗的叫法交負(fù)載均衡集群,因此我們使用“負(fù)載均衡集群”這個通俗的說法
image.png -
非對稱集群
非對稱集群中不同服務(wù)器的角色是不同的,不同服務(wù)器承擔(dān)不同職責(zé)。非對稱集群基本架構(gòu)如下:
image.png
14.異地多活架構(gòu)
異地、多活,多活就是指不同地理位置上的系統(tǒng)都能夠提供業(yè)務(wù)服務(wù),這里的“活”是活動、活躍的意思。
判斷一個系統(tǒng)是否符合異地多活,需要滿足兩個標(biāo)準(zhǔn):
- 正常情況下,用戶無論訪問哪一個地點的業(yè)務(wù)系統(tǒng),都能夠得到正確的業(yè)務(wù)服務(wù)。
- 某個地方業(yè)務(wù)異常的時候,用戶訪問其他地方正常的業(yè)務(wù)系統(tǒng),能夠得到正確的業(yè)務(wù)服務(wù)。
與“活”對應(yīng)的是字是“備”,備是備份,正常情況下對外是不提供服務(wù)的,如果需要提供服務(wù),則需要大量的人工干預(yù)和操作,花費大量的時間才能讓“備”變成“活”。
- 某個地方業(yè)務(wù)異常的時候,用戶訪問其他地方正常的業(yè)務(wù)系統(tǒng),能夠得到正確的業(yè)務(wù)服務(wù)。
保證在災(zāi)難的情況下業(yè)務(wù)都不受影響。代價很高,具體表現(xiàn)為:
- 系統(tǒng)復(fù)雜度會發(fā)生質(zhì)的變化,需要設(shè)計復(fù)雜的異地多活架構(gòu)。
- 成本會上升,一個或者多個機(jī)房搭建獨立的一套業(yè)務(wù)系統(tǒng)。
新聞網(wǎng)站、企業(yè)內(nèi)部的 IT 系統(tǒng)、游戲、博客站點等,如果無法承受異地多活帶來的復(fù)雜度和成本,可以不做,只做異地備份即可。而共享單車、滴滴出行、支付寶、微信這類業(yè)務(wù),就需要做異地多活了。
架構(gòu)模式
異地多活架構(gòu)可以分為同城異區(qū)、跨城異地、跨國異地。
- 同城異區(qū)
一個機(jī)房在海淀區(qū),一個在通州區(qū),然后將兩個機(jī)房用專用的高速網(wǎng)絡(luò)連接在一起。
同城的兩個機(jī)房,距離上一般大約就是幾十千米,通過搭建高速的網(wǎng)絡(luò),同城異區(qū)的兩個機(jī)房能夠?qū)崿F(xiàn)和同一個機(jī)房內(nèi)幾乎一樣的網(wǎng)絡(luò)傳輸速度。這就意味著雖然是兩個不同地理位置上的機(jī)房,但邏輯上我們可以將它們看作同一個機(jī)房,這樣的設(shè)計大大降低了復(fù)雜度,減少了異地多活的設(shè)計和實現(xiàn)復(fù)雜度及成本。其次,除了這類災(zāi)難,機(jī)房火災(zāi)、機(jī)房停電、機(jī)房空調(diào)故障這類問題發(fā)生的概率更高,而且破壞力一樣很大。而這些故障場景,同城異區(qū)架構(gòu)都可以很好地解決。因此,結(jié)合復(fù)雜度、成本、故障發(fā)生概率來綜合考慮,同城異區(qū)是應(yīng)對機(jī)房級別故障的最優(yōu)架構(gòu)。 - 跨城異地
部署在北京和廣州兩個機(jī)房。
有效應(yīng)對這類極端災(zāi)難事件??绯钱惖氐募軜?gòu)復(fù)雜度大大上升。距離增加帶來的最主要問題是兩個機(jī)房的網(wǎng)絡(luò)傳輸速度會降低,這不是以人的意志為轉(zhuǎn)移的,而是物理定律決定的,即光速真空傳播大約是每秒 30 萬千米,在光纖中傳輸?shù)乃俣却蠹s是每秒 20 萬千米,再加上傳輸中的各種網(wǎng)絡(luò)設(shè)備的處理,實際還遠(yuǎn)遠(yuǎn)達(dá)不到理論上的速度。
數(shù)據(jù)不一致業(yè)務(wù)肯定不會正常,但跨城異地肯定會導(dǎo)致數(shù)據(jù)不一致。如果是強(qiáng)一致性要求的數(shù)據(jù),例如銀行存款余額、支付寶余額等,這類數(shù)據(jù)實際上是無法做到跨城異地多活的。系統(tǒng)分別部署在廣州和北京,那么如果挖掘機(jī)挖斷光纜后,會出現(xiàn)如下場景:
用戶 A 余額有 10000 元錢,北京和廣州機(jī)房都是這個數(shù)據(jù)。
用戶 A 向用戶 B 轉(zhuǎn)了 5000 元錢,這個操作是在廣州機(jī)房完成的,完成后用戶 A 在廣州機(jī)房的余額是 5000 元。
由于廣州和北京機(jī)房網(wǎng)絡(luò)被挖掘機(jī)挖斷,廣州機(jī)房無法將余額變動通知北京機(jī)房,此時北京機(jī)房用戶 A 的余額還是 10000 元。
用戶 A 到北京機(jī)房又發(fā)起轉(zhuǎn)賬,此時他看到自己的余額還有 10000 元,于是向用戶 C 轉(zhuǎn)賬 10000 元,轉(zhuǎn)賬完成后用戶 A 的余額變?yōu)?0。
用戶 A 到廣州機(jī)房一看,余額怎么還有 5000 元?于是趕緊又發(fā)起轉(zhuǎn)賬,轉(zhuǎn)賬 5000 元給用戶 D;此時廣州機(jī)房用戶 A 的余額也變?yōu)?0 了。
最終,本來余額 10000 元的用戶 A,卻轉(zhuǎn)了 20000 元出去給其他用戶。
金融只能采用同城異區(qū)這種架構(gòu)。
用戶登錄(數(shù)據(jù)不一致時用戶重新登錄即可)、新聞類網(wǎng)站(一天內(nèi)的新聞數(shù)據(jù)變化較少)、微博類網(wǎng)站(丟失用戶發(fā)布的微博或者評論影響不大),能夠很好地應(yīng)對極端災(zāi)難的場景。 - 跨國異地
跨國異地指的是業(yè)務(wù)部署在不同國家的多個機(jī)房。相比跨城異地,跨國異地的距離就更遠(yuǎn)了,因此數(shù)據(jù)同步的延時會更長,正常情況下可能就有幾秒鐘了。這種程度的延遲已經(jīng)無法滿足異地多活標(biāo)準(zhǔn)的第一條:“正常情況下,用戶無論訪問哪一個地點的業(yè)務(wù)系統(tǒng),都能夠得到正確的業(yè)務(wù)服務(wù)”。例如,假設(shè)有一個微博類網(wǎng)站,分別在中國的上海和美國的紐約都建了機(jī)房,用戶 A 在上海機(jī)房發(fā)表了一篇微博,此時如果他的一個關(guān)注者 B 用戶訪問到美國的機(jī)房,很可能無法看到用戶 A 剛剛發(fā)表的微博。雖然跨城異地也會有此類同步延時問題,但正常情況下幾十毫秒的延時對用戶來說基本無感知的;而延時達(dá)到幾秒鐘就感覺比較明顯了。
因此,跨國異地的“多活”,和跨城異地的“多活”,實際的含義并不完全一致??鐕惖囟嗷畹闹饕獞?yīng)用場景一般有這幾種情況:
為不同地區(qū)用戶提供服務(wù)
例如,亞馬遜中國是為中國用戶服務(wù)的,而亞馬遜美國是為美國用戶服務(wù)的,亞馬遜中國的用戶如果訪問美國亞馬遜,是無法用亞馬遜中國的賬號登錄美國亞馬遜的。
只讀類業(yè)務(wù)做多活
例如,谷歌的搜索業(yè)務(wù),由于用戶搜索資料時,這些資料都已經(jīng)存在于谷歌的搜索引擎上面,無論是訪問英國谷歌,還是訪問美國谷歌,搜索結(jié)果基本相同,并且對用戶來說,也不需要搜索到最新的實時資料,跨國異地的幾秒鐘網(wǎng)絡(luò)延遲,對搜索結(jié)果是沒有什么影響的。


























