計算系統(tǒng)的設(shè)計其實就是在玩約束條件的游戲。一個設(shè)計良好的系統(tǒng)就像是個定制化的運輸箱:它的外部有鎖扣和手柄,但從里面裝的東西來看其內(nèi)部設(shè)計,卻往往給人印象不佳。如果要設(shè)計一個優(yōu)雅簡潔的容器,你應(yīng)該聚焦在那些最重要的因素上:尺寸、重量、;平衡和移動。這些因素以及它們之間的相關(guān)影響就構(gòu)成了設(shè)計中的有效約束條件。
在這篇文章中,我將描述了一種對系統(tǒng)數(shù)據(jù)的形狀和流的約束條件進行分析的方法,并將它應(yīng)用在真實世界的例子中?;诩s束條件的方法和物流領(lǐng)域中的格德拉特(Goldratt)理論相似。根據(jù)我的經(jīng)驗,最成功的系統(tǒng)設(shè)計師和容量規(guī)劃師傾向于依據(jù)具體的約束條件來思考計算系統(tǒng),即使他們可能沒有說出聲來。
相比從基準(zhǔn)(benchmark)外推,或者爭論“并發(fā)用戶”的定義,使用約束條件方法要有用的多。它能在編碼之前揭示可能的系統(tǒng)瓶頸及其相互的依賴關(guān)系。這些知識對于理性地選擇設(shè)計方案至關(guān)重要。通過實踐你甚至可以對別人,比如競爭對手的系統(tǒng)做出敏銳的猜測,而這也很簡單,只需要你觀察它是如何運行的。
這個技巧在于先建立數(shù)據(jù)的尺寸和重量,然后聚焦于它是如何流動的。計算機其實只做兩件事情:讀入數(shù)據(jù)和寫出數(shù)據(jù)。性能決定了多少數(shù)據(jù)可以移動,以及它們移動到哪里來完成任務(wù)。說起來似乎很容易,但這需要掌握基礎(chǔ)的計算理論。每個計算機等同于一個圖靈機(Turing Machine),而所有圖靈機做的事情就是在磁帶上移動符號,所以它的吞吐量取決于符號移動的速度。從小的微米級的CPU內(nèi)部到大的橫跨世界的分布式數(shù)據(jù)庫,這個推論仍然成立。幸運的是,這里的數(shù)學(xué)方法簡單明了。
可以迅速丟棄壞的方案,而無需實際創(chuàng)建它們,這種能力對于好的系統(tǒng)編程有決定性的意義。它部分是靠直接,還有一些經(jīng)驗,但最主要的是算法。
工作集尺寸(Working set size):這是系統(tǒng)在正常操作時需要處理的一組數(shù)據(jù)。復(fù)雜系統(tǒng)通常會有多個不同的工作集,但其中一至兩個是最主要的。在類似郵件或者是新聞饋入的流應(yīng)用中,當(dāng)前處理的工作集要比總體的工作集小得多。人們幾乎很少會去訪問幾周前的消息,所以把它們考慮成由另一個系統(tǒng)處理也許更好。更多時候,情況并不如此簡單,很難在“熱”數(shù)據(jù)和“冷”數(shù)據(jù)之間畫一條清晰的界限。所以,用概率帶(probability band)來思考這個問題會很有幫助:比如過一段給定的時間后,不同部分的數(shù)據(jù)被使用的概率會是多少?會有多少個概率帶?它們是如何分布的?在初始分析中,你可以只關(guān)注工作集的大體尺寸,而不是它的細節(jié)特征。然而,這些細節(jié)會在后面主動找你的。
平均事務(wù)尺寸(Average transaction size):這個可以理解為系統(tǒng)執(zhí)行單個事務(wù)時的工作集。那么系統(tǒng)完成一個事務(wù)要接觸到多少數(shù)據(jù)呢?下載一個圖片和運行一個網(wǎng)絡(luò)搜索,其發(fā)送到客戶端的回答包括同等規(guī)模的數(shù)據(jù)。但是,它們在后臺處理時接觸到的數(shù)據(jù)卻差別很大。注意我使用了“事務(wù)”這個詞來表示不相同的工作,這個觀點同樣適用于大的分析作業(yè)。
請求速率(Request rate): How many transactions are expected per hour / minute / second? 每個小時/分鐘/秒會有多少個事務(wù)發(fā)生呢?會有峰值時間,或者比較穩(wěn)定的需求? 如果是搜索引擎,每分鐘每個人可能會有5到10次的查詢需要處理。如果是在線電子書,它可能是一個持續(xù)但較低的流量。如果是游戲,那每個人每秒種可能會有多個事務(wù)要處理。簡單說,這些都是預(yù)期的并發(fā)量。并發(fā)量和事務(wù)尺寸的結(jié)合 決定了系統(tǒng)數(shù)據(jù)流的主要因素。
更新速率(Update rate): 這是來衡量數(shù)據(jù)增加、刪除和編輯的頻率。郵件系統(tǒng)有高的增加速率,低的刪除速率和幾乎是零的編輯速率。而廣告拍賣的用例在這三種速率上都異乎尋常的高。衡量更新速率是否值得擔(dān)心有個有用的方法,就是把它和讀取的并發(fā)量做一個對比。
數(shù)據(jù)增長的速率和工作集大小或數(shù)據(jù)保留策略也是相關(guān)的。0.1%的增長速率意味著數(shù)據(jù)要保留大概三年(365 * 3大約是1,000)),反之亦然。而1%的增長率則意味著數(shù)據(jù)保留100天。
一致性(Consistency): 一次更新必須在多快的時間內(nèi)傳播到整個系統(tǒng)?對于關(guān)鍵字廣告報價,幾分鐘的傳播時間是可以接受的,而股票交易系統(tǒng)必須在毫秒級完成. 一個評論系統(tǒng)通常期望在一兩秒內(nèi)顯示新的評論, 這需要后臺瘋狂的工作,以給評論者產(chǎn)生即時性的錯覺。當(dāng)更新速度是請求速度中最主要的部分時,一致性就是個關(guān)鍵因素。當(dāng)傳播的更新對于業(yè)務(wù)特別重要時也同樣如此,比如賬號注冊或者價格和存貨發(fā)生了變化。
位置(Locality): 一個請求需要訪問工作集中的哪些數(shù)據(jù)? 這部分的數(shù)據(jù)如何去定義? 不同請求是否有重疊的部分?極端情況下會使用搜索引擎來回答這些問題: 即用戶可以從系統(tǒng)中任何地方來查詢到想要的數(shù)據(jù)。在郵件應(yīng)用中,用戶被確保只能訪問他們的收件箱,相對整體數(shù)據(jù),這是個小的且定義良好的數(shù)據(jù)片段。在另一個實例中,你可能需要無重復(fù)數(shù)據(jù)的存儲來保存郵件附件,并讓其成為熱點。
計算(Computation):你需要用什么樣的數(shù)學(xué)方法來運行數(shù)據(jù)?數(shù)據(jù)可以預(yù)先計算和緩存嗎?你會在大數(shù)據(jù)集上進行交叉嗎?你是將計算貼近數(shù)據(jù),還是相反?為什么?
時延(Latency):事務(wù)多快 可以返回成功或失敗呢?用戶在搜索航班或者進行信用卡交易時,似乎可以接受幾秒鐘的處理時間。網(wǎng)絡(luò)搜索必須要在幾百毫秒內(nèi)可以返回;而系統(tǒng)調(diào)用外部依賴的API時則希望可以在100毫秒或更少的時間內(nèi)返回結(jié)果。除了時延,考慮方差也是很重要的。正如論證的那樣,90%的查詢在0.1秒以內(nèi),剩余的在2秒以內(nèi)的情況要比所有請求都在0.2秒內(nèi)完成的情況要差。
找到瓶頸
找到一個你想分析的系統(tǒng),然后填寫出上述的那些約束條件,并且草擬一個方案能滿足這些條件。下來問自己最后一個問題:決定性的操作是什么?換句話說,最基本的瓶頸在什么地方?哪些部分必須仔細考量?
當(dāng)你大聲說出來答案時,雖然聽起來可能平淡無奇,但是確定一個系統(tǒng)的真正瓶頸對于 認知事物有極大的幫助。偶發(fā)性的瓶頸往往會形成堆積,修正一個會激活另一個,但是它們不會對你設(shè)計的基本前提形成顛覆性的威脅?;A(chǔ)性的瓶頸則難于修復(fù),也很難識別,因為它們是由創(chuàng)建系統(tǒng)時基于的一些自然事實或者一個強假定引起的。一個無窮快的網(wǎng)址仍然受限于光的速度,火箭也會受限于提升自身燃料重量的需求。保持這種思考性的實驗直到你完全理解最基礎(chǔ)的瓶頸是什么?以及為什么?
我們可以打個比方,你有個披薩店,想賺更多的錢。如果購買的人排長隊,你可以把訂單登記的數(shù)量增加一倍。如果是披薩送貨時遲到,你可以規(guī)劃一個更好的工作節(jié)奏,或者為送貨員配備車輛。甚至你可以嘗試提高烤箱的溫度。但從根本上說,披薩店的瓶頸是烤箱的尺寸。即使你把其他事情都做好,如果烤箱容量不擴張,你也無法生產(chǎn)出更多的披薩。當(dāng)然你也可以再購買一個烤箱,不過你必須事情想清楚,把這個新家伙放到什么地方。
如果你不能清晰地發(fā)現(xiàn)系統(tǒng)的基本瓶頸,那么就去改變系統(tǒng)的約束條件,看看它的響應(yīng)有什么變化。比如你必須將延遲降低10倍,看看會發(fā)生什么?比如你只用一半數(shù)量的計算機?如果放松一致性的約束,又要靠什么樣的技巧才能僥幸成功?通常認為初始約束是真實和靜止的,但實際中它們很少如此。問題中的創(chuàng)造力遠比答案中的創(chuàng)造力更有利用價值。
如果你想了解某件事情,把它放在極端條件下觀察或者檢查它的對立面。
——Col. John Boyd
影視流用例
讓我們將上面的方法應(yīng)用到比披薩店更復(fù)雜的案例中,即視頻流服務(wù)??梢韵胂笏且粋€小規(guī)模的Hulu或Netflix,視頻庫容量為十萬個,平均60分鐘的時長,500 kbps的比特率。在高峰時間,大約會有一百萬人觀看。
工作集尺寸: (100 k視頻)*(60分鐘)*(60秒)*(500 kb /秒)/( 8位字節(jié)),算出來大約超過20 TB的數(shù)據(jù)。公式中最敏感的數(shù)字是比特率,改變比特率可以縮小或膨脹總的數(shù)據(jù)大小。
工作集的概率帶依賴于視頻受歡迎程度的分布,它(可能)是個長尾。假設(shè)所有視頻中,受歡迎程度前100個的視頻占播放總數(shù)的10%,排名在前1000個的視頻占20%,排名在前10000個的視頻占35%。而倒數(shù)第三的視頻的占比很可能已經(jīng)無足輕重,你甚至不必去留意它。所以,人們很容易完全忽略長尾。然而,正如我們后面將看到的那樣,這將是一個mistake。
平均請求尺寸(Average request size): 大約是(3600秒)*( 64 kbps),或者幾百MB。在實踐中,流的平滑取決于能夠把數(shù)據(jù)以略高于其消耗的速度推到客戶端去,并且很可能都是以小數(shù)據(jù)塊來完成。為了使問題簡化,我們特意忽略了發(fā)送到客戶端過程中,涉及流量整形的那些繁雜的問題。
請求速率(Request rate): 和較小請求尺寸的系統(tǒng)相比,其請求速率會相當(dāng)?shù)牡?,但是仍會有高并發(fā)的請求??梢云谕脩魹g覽是多個短脈沖,而媒體流則是長時間持續(xù)的。高峰時段系統(tǒng)每秒排出的數(shù)據(jù)將達到每秒0.5TB。
更新速率(Update rate): 和請求速率相比,更新速率近乎于零,這是因為視頻基本上都是專業(yè)制作。但如果這是YouTube,那么將會是一個完全不同的故事。
一致性(Consistency): 因為在很大程度上這是個只讀的系統(tǒng),所以這一點可以被忽略掉。
位置:用戶可以觀看任何一個電影,當(dāng)然同一時刻只能有一個。從相反的方向來看這個問題,你會發(fā)現(xiàn)來自不同的地方的用戶會在同一時刻訪問相同的電影。
計算(Computation): 計算量的大小取決于視頻是否是動態(tài)生成編碼。如果不是這樣,那么計算的主要工作就是把數(shù)據(jù)放到管道中。
延遲(Latency): 這是個有趣的問題。最壞的情況是不停的進行頻道切換或者在視頻播放時中不停的跳躍播放位置。在實際的影片服務(wù)中,您可能已經(jīng)注意到,切換不同的媒體流或者在一個視頻中跳躍播放位置需要在一兩秒鐘內(nèi)完成,長于這個時間將是不可接受的。
現(xiàn)在讓我們來概述一下可以滿足這些約束條件的系統(tǒng)??偣?0TB需要保存的數(shù)據(jù)量,看上去不算太大;500Gbps的請求速率,看上去數(shù)據(jù)服務(wù)量還是挺多。如果我們使用當(dāng)前(2015年)16核的文件服務(wù)器,它可以輕松完成7 Gbps的有效網(wǎng)絡(luò)數(shù)據(jù)吞吐,所以我們需要至少50個這樣的服務(wù)器,每個上面配置128 GB的RAM,還有2 TB的存儲,這樣很容易容納整個的工作集數(shù)據(jù)。
雖然我們可以把數(shù)據(jù)存儲起來,但我們可以移動它們嗎?讓我們再來看看歡迎度曲線,前100個視頻占了總需求的10%,所以你會想要在每個服務(wù)器存儲這100個視頻的副本。實際中你可能會對前一個視頻都這樣做,它們占了總帶寬的三分之一,但只用掉了10%的存儲空間。如果有足夠的RAM和一點運氣,那么受歡迎的視頻幾乎完全可以從內(nèi)存中提供服務(wù)。
這樣,還剩下9萬個視頻,處于長尾右端的3萬個視頻觀看次數(shù)很少,所以無關(guān)緊要。但長尾中間的6萬個視頻占了總業(yè)務(wù)流量的2/。所以需要考慮如何為它們服務(wù),同時保持延遲條件的約束?由于RAM已經(jīng)被最受歡迎的視頻占用,所以接下來就要算算存儲層額可以支持多少500 kbps的業(yè)務(wù)流量并發(fā)。具體的設(shè)計可能要進行測試后才會出來,但最終很可能是幾百個磁盤驅(qū)動器并行工作來達到好的結(jié)果。
從這里我們可以得出結(jié)論:視頻服務(wù)的基本瓶頸是隨機尋道時間,在設(shè)計上這等同于那些小的金屬機械臂可以來回移動的速度。起控制作用的約束條件是人們觀看視頻的歡迎度曲線,這個是你無法改變的。
還有其他的設(shè)計方案可能會解決這個問題。例如,在每個服務(wù)器上配置1 TB的RAM,而不是2 TB的磁盤。使用今天或明天的SSD技術(shù)也可以圓滿的解決這個問題。這里用到的數(shù)字可能不是很準(zhǔn)確,而且肯定會隨著時間的推移而改變。關(guān)鍵是發(fā)現(xiàn)那些最重要的具體細節(jié),并重點討論它們。
人臉識別用例
有時候,系統(tǒng)會被一個特定操作嚴重主導(dǎo),以至于幾乎其它操作相比它都無足輕重??梢耘e一個有趣的案例,即人臉識別。記住不是人臉檢測,人臉檢測僅僅是在圖像中找到人臉,而決定在給定照片中的人是誰,則需要和照片庫中照片進行比較。
人臉識別首先需要對已知人群的照片進行預(yù)處理,以便對每個人的基本特性生成一種抽象描述,這種抽象描述有個非常奇怪的名字,叫“特征臉(eigenface)”。一個特征臉是一個固定大小的數(shù)據(jù)塊,通常小于100 kB,它可以由一些聰明的算法生成。生成特征臉的主要好處是可以相對低廉的計算任意兩個人臉之間的相似度得分。得分越高,兩個人臉輪廓特征就越接近,從而你識別出這個人是誰的可能性更高。
假設(shè)你有一個對應(yīng)五千萬個體的人臉特征庫,它們可能來自國民護照,或者駕照數(shù)據(jù)庫,或者是世界上最大的年鑒。系統(tǒng)每秒會有數(shù)百個查詢照片流請求進來,這些實時地流請求來自邊境機構(gòu)的護照控制需求。必須在1秒或更少的時間內(nèi)為每張照片找到相似度排名前十的匹配者。好了,我們開始分析:
工作集尺寸:工作集尺寸為:5千萬 * 100KB,總共5TB。熱數(shù)據(jù)和冷數(shù)據(jù)短期內(nèi)無法區(qū)分,并且可能并不存在這樣的區(qū)分。
平均請求尺寸: 每個請求系統(tǒng)進來的數(shù)據(jù)是一張照片,他可以被縮減成100KB固定尺寸的特征臉,但每個請求需要訪問的系統(tǒng)數(shù)據(jù)可能會非常高。
請求速率:每秒300個。
更新速率: 具體情況暫時還不清楚,但是可能次數(shù)較低,而且應(yīng)該是在預(yù)處理時的批處理操作。
一致性:在更新速率低的情況下,這個可以暫時忽略。
位置:我們必須掃描庫中的5千萬資料,并且給匹配度排名,這個想法有些不切實際。我們需要找到一種方法盡可能多得省略一些工作。
計算:閱讀實際的人臉檢測算法是相當(dāng)迷人的,但對于這個練習(xí)卻并不是很重要。我們做一個假設(shè)即可,即完整的比較兩個特征臉需要10毫秒的CPU時間。
延遲:必須在1秒或更少的時間內(nèi)完成,這是個剛性需求。
滿足低延遲需求的唯一方法是在執(zhí)行在給定的搜索時,大幅減少特征臉全面對比的數(shù)量。但正如腸道檢查那樣,這涉及到可能性的范疇,即在這個問題上是否可以投入足夠的計算資源?答案很可能是No。每秒特征臉對比的次數(shù)=5000萬* 10毫秒* 300,這大約需要每秒5000 CPU-years的計算能力。我看過一些大型集群,但都達不到這樣的規(guī)模要求。
所以我們需要用些聰明的方法來減少工作。這個領(lǐng)域中比較活躍的研究是一種快速搜索【特征臉?biāo)饕康姆桨福峭ㄟ^摘要我們知道其加速線性而不是對數(shù)的(所以可能無法滿足我們的性能需求)。谷歌有一個通用的圖像相似度搜索方案,這個理論上也許是可行的,但是不一定是可用的技術(shù)?,F(xiàn)在我們先停止調(diào)查,嘗試一下別的東西吧。也許我們可以基于人的眼睛顏色、年齡或性別來消除大部分的候選人。舉個例子來說,對比一個30歲的女人和一個男孩子的照片是沒有意義的。
假設(shè)可以通過啟發(fā)式和低成本的技巧來縮小候選人的數(shù)量,比如對于一個給定的查詢照片只需要對比1000個候選人。這相當(dāng)于用10個CPU來解決10,000毫秒(譯者注:1000個候選人乘以10毫秒)的計算問題以讓延遲控制在1秒,如果添加更多的資源,完成任務(wù)就沒有問題。如果每個用12個CPU,那么300 * 12意味著我們總共需要3600個CPU來處理請求,這大約是250臺服務(wù)器或是六個機架。這樣的計算機集群其重量大概是三噸,其計算能力以任何標(biāo)準(zhǔn)衡量都是相當(dāng)高的,但為了這個重要的項目還是值得的。
我們現(xiàn)在可以計算它了,但是我們可以保存數(shù)據(jù)嗎?5TB對于RAM來說是相當(dāng)多的。但從另一方面來說,我們有服務(wù)器集群來提供這些內(nèi)存。將庫中的特征臉數(shù)據(jù)分布到集群的RAM中有點類似Memcached的方案。
好了,現(xiàn)在讓它動起來吧。噢。它看起來走不了。
我們可以存儲它,但是我們可以移動它嗎?我們忘記更新平均請求尺寸了。如果單個請求訪問1000個特征臉,這些特征臉數(shù)據(jù)是隨機分布的,這相當(dāng)于將100 MB的數(shù)據(jù)從它所在的Memcached服務(wù)器移動到另一個負責(zé)特征臉對比的CPU所在的服務(wù)器上。假如那個時間每秒有300個請求,那我們就會面臨240gbps網(wǎng)絡(luò)帶寬消耗,這是個致命的風(fēng)險。對于每臺服務(wù)器來說,這接近于1gbps這個令人不安的數(shù)據(jù),而且此時CPU已經(jīng)忙于特征臉的比較而無法應(yīng)對這么高的網(wǎng)絡(luò)傳輸負載。
我們需要將計算放在數(shù)據(jù)存儲的地方,而不是將數(shù)據(jù)傳輸?shù)接嬎愎?jié)點。在開始比較之前,我們準(zhǔn)確地知道哪1000個特征臉需要對比。Memcached對特征臉鍵值采用直接哈希的方式,所以特征臉存儲的服務(wù)器是很容易得知的,下來就可以將具體的請求路由到這個特定的服務(wù)器上。每個服務(wù)器基于本地數(shù)據(jù)會做4或5次比較,并返回它們的相似度得分。整體的相似度得分的列表可以很容易地組裝起來并進行排序。這種方案下,唯一的網(wǎng)絡(luò)流量將是查詢時特征臉照片所占的100 KB,乘以服務(wù)器的數(shù)量250,估算成300倍,總共60gbps,這個數(shù)字雖然很高,但是是可行的。進一步,可以使用更聰明的數(shù)據(jù)分布方案來避免數(shù)據(jù)查詢展開到整個集群中。
系統(tǒng)的基本瓶頸就是掃描那些特征臉,到目前這點可能看得還不是很清晰。這是個比較難的問題,我們幾乎沒找到一個合理的解決方案,甚至我們還沒有考慮識別的準(zhǔn)確性是否足夠有用。這種人臉識別的分析在重要細節(jié)幾乎肯定會出錯,但從一些懂行的人給出的評論來看,方案大致是正確的。我故意選擇了這個我不太了解的用例來演示如何約束分析如何幫助你分析系統(tǒng),即使(特別)你不是一個專家。
想象一個專家過來告訴你“為什么不利用GPU呢?他們可以將特征臉的對比提速十倍!”你可以回答“聽上去很有趣,但是它對網(wǎng)絡(luò)帶寬問題有何幫助呢?”(實際上可能會有幫助,如果它能減少機器數(shù)量)。如果有人說“我有一個辦法來索引特征臉!”你可以回答“這很有趣,但前提是它能幫助我執(zhí)行不到1000次完整的比較。順便說一下,你應(yīng)該和懂GPU的那幫家伙聊聊。”你也知道應(yīng)該密切注意說這樣話的一些人,即 “我可以使用比100KB更小的尺寸來準(zhǔn)確地代表一個人的臉。”
這其是基于約束條件分析方法的真正價值,即幫助你更好理解問題的物理意義,從而揭示出那些需要創(chuàng)造性的地方。像特征臉技巧那樣,它允許你通過標(biāo)準(zhǔn)形式來呈現(xiàn)復(fù)雜的圖片,從而可以在對比中很快地接受或拒絕。
科學(xué)不僅僅是探索為什么(Why),而是為什么不(Why not)?
— Cave Johnson
結(jié)束語
類似這種大數(shù)據(jù)系統(tǒng)分析的工作有幾個技巧值得去練習(xí)。能夠通過不完整的數(shù)據(jù)進行快速評估是至關(guān)重要的。例如,谷歌擁有多少臺服務(wù)器呢?(可以按照電力消耗來估算)。另一個很好的技能是能丟棄推理中那些有缺陷的思路,并及時對大腦中的想法進行刷新。Jeff Dean有許多非常好的在線談?wù)摪?ldquo;你應(yīng)該知道的數(shù)字”以及如何考慮大型系統(tǒng)的設(shè)計。
可以在現(xiàn)有系統(tǒng)上進行約束條件分析,然后來查找答案,這對于掌握技能是個很好的方式。在本文中,我回避了那些有高更新速率或高一致性約束條件的案例。但你可以花一個小時左右的時間來分析一些類似的商業(yè)系統(tǒng)以增強了解,比如2004年左右時的AdWords、2005年時的YouTube或者2005年的Flickr。關(guān)于這些系統(tǒng)都有一些很好的評論和討論,而且是系統(tǒng)的創(chuàng)建者寫的。但是建議你在獨立的解答之前先不要看這些文章。