7大知名互聯(lián)網(wǎng)的系統(tǒng)擴展經(jīng)驗
本文出自澳大利亞一位ID為Dodgy Coder的程序員2012年4月的博客文章。他從High Scalability上整理和總結(jié)了Google、YouTube、Twitter、Amazon、Ebay、Facebook和Instagram等7家知名互聯(lián)網(wǎng)的系統(tǒng)擴展經(jīng)驗。值得注意的是,有些資料時過境遷,已經(jīng)不再反映最新情況,但是核心的理念和許多具體經(jīng)驗還是非常寶貴的學習資料,值得一讀。
不難發(fā)現(xiàn),這7個公司都有以下共同的6大理念:
保持簡單——隨著時間推移,復雜性會自然出現(xiàn)。
自動化一切——包括災難恢復。
不斷迭代——想擴展到更高水平?必須準備好忍痛棄用現(xiàn)在能工作的某個組件。
選擇合適的工具——但也不怕自己動手打造。
使用緩存——在適當?shù)牡胤健?/p>
根據(jù)場景,在數(shù)據(jù)的一致性和可用性之間做取舍。
下面來分別看一下7大公司的經(jīng)驗吧。

一、 Google
可靠的存儲(Reliable Storage)
可靠、可擴展的存儲基本上是任何應用程序的核心。GFS(Google File System)是Google的核心存儲平臺——它是一個大型分布式結(jié)構化的日志文件系統(tǒng),Google在其中存放了大量的數(shù)據(jù)。為什么會自建系統(tǒng),而不是使用其他已有的產(chǎn)品?因為Google需要對系統(tǒng)有絕對的掌控力,同時這個平臺也正是Google之所以成為Google的地方。GFS使他們獲得了跨數(shù)據(jù)中心的高可靠性、擴展到數(shù)以千計個節(jié)點的能力、提供巨大的讀寫帶寬、支持以GB為單位的大數(shù)據(jù)塊處理和跨節(jié)點分布操作以降低瓶頸的高效技術。
基礎設施成為競爭優(yōu)勢
Google可以更快、更便宜并且在規(guī)模上罕有匹敵地發(fā)布新的互聯(lián)網(wǎng)服務。許多公司與Google的想法并不相同,他們把基礎設施看成負擔,花錢的事兒。新舊兩類公司使用的技術完全不同,在系統(tǒng)開發(fā)上也少有共識。
在平臺的基礎上構建應用程序
大平臺方式有一個經(jīng)常被忽略的優(yōu)勢,就是初級開發(fā)者也能很快并自信地開發(fā)健壯的應用程序。如果每個項目都需要建立分布式基礎設施,那么你很快將會陷入困境,因為懂得這么去做的開發(fā)者非常少。協(xié)同效應并不總是空談,從整個系統(tǒng)上著眼改善,可以幫助到建立在這個系統(tǒng)上的所有應用程序或項目。比如:改善了文件系統(tǒng)就可以讓所有項目都立即而且透明地(指上層開發(fā)者和使用者都無需操心)獲益。如果每個項目都使用不同的文件系統(tǒng),那么在整個技術棧上的改進將不會帶來持續(xù)不斷的增益。
自動化和恢復
建立自管理系統(tǒng),讓工作不需要停機進行。這樣允許你更容易地進行以下操作:在多臺服務器間重新調(diào)配資源、動態(tài)添加容量、將機器下線以及從容地處理升級。
建立不斷進化的基礎設施
并行地執(zhí)行一個耗時(CPU綁定的)操作,并取優(yōu)勝者。這尤其適合在CPU富余而IO不足的情況。
不要忽視學術界
學術界有很多很棒的思想,只不過還沒有進入生產(chǎn)環(huán)境。你現(xiàn)在看到Google所做的事情其實都并不新鮮,只是沒有大規(guī)模部署而已。
考慮數(shù)據(jù)壓縮
當由許多機器組成的大型集群受限于IO時,壓縮不失為一良策。 #p#

二、 YouTube
越簡單越好
尋找問題領域的最簡解決方案。這里存在許多復雜的問題,但是選擇解決方案的首要前提就是不能復雜。隨著時間的發(fā)展,復雜性會一直存在,而最簡單和最松散的解決方案是始終適用的。這樣做的原因是保持解決問題的靈活性,反之你則會把自己逼入角落。你將會失去對程序的控制,同樣當你試圖解決時,問題將變的越來越復雜,你會變得無路可走。
欺騙:知曉如何在數(shù)據(jù)上作假
最快的函數(shù)調(diào)用就是根本上沒有發(fā)生。當你需要做一個持續(xù)增加的計數(shù)器時,比如說一個瀏覽計數(shù),你需要為每次的更改做數(shù)據(jù)庫調(diào)用?;蛘吣憧梢悦扛粢欢蔚臅r間做一次調(diào)用,或者是一個隨機數(shù)量做更改——但是人們可能就會認為它是實時顯示的。你必須要知道如何在數(shù)據(jù)上作假。
抖動(Jitter)
如果你的系統(tǒng)不存在抖動,將會因為用戶在同一時間對同一個資源進行請求產(chǎn)生Thundering Herd(“驚群效應”)。對于一個流行的視頻,YouTube會盡可能的為其做緩沖。最流行的視頻可能會緩沖24小時。如果所有緩存同時到期,將會造成上面所說的Thundering Herd。通過抖動,你可以設置隨機的時間(18-30小時)。這將阻止事情在同一個時間發(fā)生,并且保證很長時間內(nèi)請求的順利完成。
近似正確
用戶所見就是你系統(tǒng)的狀態(tài)。如果用戶看不到你系統(tǒng)中存在的偏移和不一致,那么這些問題從本質(zhì)上來說根本“不存在”。如果你正在一個頁面上發(fā)布評論,而這時候某些用戶剛好打開了這個頁面,那么這些用戶在半秒內(nèi)可能根本看不到你的評論,然而那些閱讀這個頁面的用戶根本不會在意這個事情。這種情況就允許你稍微的進行“作弊”,因為你的評論并沒有達到全局一致性。如果真的去做這個全局上的一致性,那將會投入大量的開銷,同樣也是牛刀殺雞——因為你并不是在做金融系統(tǒng),所以你可以作弊。 #p#

三、 Twitter
實現(xiàn)API
Twitter的API流量是Twitter網(wǎng)站的10倍,API是Twitter增長用戶數(shù)量最重要的手段。保持服務的簡單,允許開發(fā)者在自己基礎設施上建立服務,并且想出比Twitter自己更好的應用程序點子。所謂眾人拾柴火焰高,集思廣益才能做更好的創(chuàng)新。所以開放你的應用,并且讓其保持簡單,這樣就可以和其他人的應用程序進行整合。(當然,后來在贏利壓力下,Twitter過河拆橋,將有史以來最有活力的API生態(tài)鏈生生干掉了。)
使用你清楚的東西
Twitter使用了一堆消息傳送。對用戶發(fā)布的消息進行排隊,然后分發(fā)給指定的用戶。Twitter最主要的功能就是扮演消息傳遞的橋梁,架起不同格式(SMS、Web、IM等等)之間的消息傳送。在后臺同步發(fā)送消息去清除朋友的緩存,而不是單獨的進行。Twitter開發(fā)者對Ruby最為熟悉,所以他們拋棄DRb轉(zhuǎn)至Starling(一個Ruby編寫的分布式隊列系統(tǒng))。分布式隊列系統(tǒng)將隊列寫入磁盤,以防止系統(tǒng)崩潰。以Twitter的經(jīng)驗,大部分的性能提升不是語言的選擇而是應用程序的設計。(這一點也不完全正確了,Twitter因為性能,后來從Ruby整個遷移到了Scala/JVM。)
知道何時進行緩存以及緩存什么
舉個例子,獲得你朋友的狀態(tài)是很復雜的,包括了安全等多個隱患。所以,取代對數(shù)據(jù)庫進行查詢,朋友的狀態(tài)將會被放入緩存。永遠都不會接觸到數(shù)據(jù)庫。90%的請求都是API請求,所以他們在前端基本上不做任何頁面緩存。因為Twitter的頁面都對時間敏感,這樣做(緩存頁面)沒有任何好處。 #p#

四、 Amazon
使用SOA
Amazon的架構都是松耦合的,并且圍繞著服務建立。一個面向服務的體系結(jié)構(SOA),基于他們可以快速及獨立的建立軟件的多個組件,允許他們更快的向市場上投放。Amazon.com Web頁就是一個類似的應用程序服務器。這樣的話這個應用程序同時服務了網(wǎng)絡服務接口、用戶服務應用程序以及賣方接口。
使用API打造你的系統(tǒng),你將圍繞你的應用程序建立起一整套的生態(tài)系統(tǒng)。圍繞著服務展開將給你更高的靈活性——你可以并行的進行操作,因為所有的輸出都是一項服務。禁止客戶端直接對數(shù)據(jù)庫進行訪問,因為不會涉及到客戶端,所以你的服務將擁有更好的擴展性和可靠性。這點很像谷歌的改變某個組件讓建立在整個系統(tǒng)或平臺上的應用程序都獲益。
根據(jù)場景在數(shù)據(jù)的一致性和數(shù)據(jù)的可用性之間做取舍
既然擴展你就必須做分片,所以你必須為特定的系統(tǒng)做高一致性或者高可用性的選擇。你必須發(fā)現(xiàn)有效性和一致性上的重疊部分,根據(jù)服務的需求選擇一個合適的方案。舉個結(jié)賬系統(tǒng)的用例:你總是希望將請求作為購物車的添加項,因為它產(chǎn)生了收入。在這個用例中,你就選擇了高可用性。錯誤就隱藏在了客戶方面,并且由其提出:當客戶進行提交時,你必須對一致性進行重點對待,因為不同的服務(信用卡處理、運輸、操作、報告)都將同時訪問數(shù)據(jù),并且每個都有各自數(shù)據(jù)一致性的需求。
擁抱失敗
對失敗抱平常心,它可能會經(jīng)常出現(xiàn),所以擁抱它。比如,使用一個快速重啟和快速恢復方案。選用一個合適的數(shù)據(jù)傳輸,服務正常運行的幾率將接近100%。建立一個自我修復、自我組織、無人值守類型的操作。
只用你需要的
讓設計保持簡單,確定設計中沒有隱藏的需求及依賴性。將技術程度降到最低,你只需要一些解決問題的必須技術。確保這些技術不會帶來更多的復雜性,慎重甚至是不選擇一些特定的方法或者技術堆棧。有些地方他們使用jboss/java,但他們只選用Servlet,而不使用余下的幾個J2EE框架。使用C++來處理請求,使用Perl/Mason來建立目錄。
根據(jù)客戶的反饋來指定決策
使用測量和客觀的討論去區(qū)分好壞。給客戶一個切實的選擇來測試哪個更好,并且通過這些測試制定決策。這點通常使用類似A/B測試和Web Analytics等技術實現(xiàn)。如果你產(chǎn)生決策上的問題,那么將其編碼,讓更多的人使用,從而清楚哪個選擇才是你真正想要的。
擴展性即競爭優(yōu)勢
和Google一樣,基礎設施同樣是Amazon競爭優(yōu)勢所在。他們可以簡單的在原始服務上建立非常復雜的應用程序。他們可以獨立的進行擴展操作,保持無與倫比的系統(tǒng)可用性,在不需要大規(guī)模的重配置下就可以快速的推出新服務。 #p#

五、 eBay
切分一切
如果你不能對其進行切分,那么你就不能對其進行擴展。通過功能和數(shù)據(jù),將所有東西都切割成容易控制的組塊。
處處異步
通過事件驅(qū)動隊列和傳輸管道,連接起所有的組件。
擁抱故障
監(jiān)視所有發(fā)生的事情,別間斷服務——即使有些部分開始發(fā)生故障。最小化和控制依賴性,使用抽象的接口和虛擬化技術,組件中包含一個SLA,用戶從SLA違規(guī)中恢復。自動化所有事情,組件需要自動調(diào)整,而系統(tǒng)則需要自我調(diào)節(jié)和完善。
擁抱不一致
在需要使用CAP原理的地方挑選好每個特征,如果選擇非分布式事務,不一致性可以通過操作順序來最小化,通過異步恢復和調(diào)整實現(xiàn)最終一致性。
保存所有的數(shù)據(jù)
數(shù)據(jù)驅(qū)動最佳的機遇、預測和推薦的發(fā)現(xiàn),所以保存所有。清楚哪些數(shù)據(jù)是有權威的,哪些數(shù)據(jù)沒有,進行不同的對待。
基礎設施:給指定的工作分配合適的工具
需要最大化的使用每個資源:數(shù)據(jù)(內(nèi)存)、處理(CPU)、時鐘時間(延時)等。沒有通吃的策略,區(qū)分規(guī)模對待。由商用、工業(yè)服務器共同組成。 #p#

六、 Facebook
擴展需要多次的迭代
解決方案通常是在工作的開始時提出,然而隨著發(fā)展你必須對其進行修改——已經(jīng)使用了一年的方案,以后可能不再適用。一個好的例子就是圖片,F(xiàn)acebook現(xiàn)在(文章撰寫時)每秒需要服務12億張圖片。第一代的思想就非常簡單,沒有考慮到擴展到如此規(guī)模,只注重功能上的實現(xiàn)。Uploader會將文件儲存為NFS格式,而原數(shù)據(jù)將會保存在MySQL中。這個方案只用了3個月,但是這并不重要,在上市時間上他們贏得了巨大的競爭優(yōu)勢,同樣功能上的特點比深思擴展方案來的更加重要。第二代則使用了不同的訪問方式對其進行優(yōu)化,鑒于較小的圖片訪問頻度會比較高,所以對其使用了緩存,他們同樣開始使用CDN(內(nèi)容分發(fā)網(wǎng)絡)。第三代則是一個overlay系統(tǒng),讓Facebook可以在原有的文件系統(tǒng)上使用BLOB存儲。圖片被存儲到一個二進制的BLOB,因為你清楚BLOB中圖片的字節(jié)偏移量,所以每張圖片對磁盤只進行一次IO操作。
不要重復設計一個方案,讓其保持簡單
在你對系統(tǒng)進行橫向擴展時,只使用你需要用到的。找到方案中需要重做的地方,進行優(yōu)化,或者著手重新建立堆棧中需要修改的部分。Facebook花費了大把的時間去優(yōu)化PHP,最終完成了HipHop的編寫,完成了PHP到C++的轉(zhuǎn)換,這為他們節(jié)省了大量的內(nèi)存和CPU開銷。然而你不需要從第一天就著手做這個事情,在完全重寫一門語言之前你需要做的是聚焦產(chǎn)品的特性。
針對工作選用正確的工具,并且接受這個選擇所帶來的開銷
如果你需要使用Python,并選擇了它進行開發(fā),但是必須要認識到這個選擇是有開銷的:通常是部署、監(jiān)視、運營等方面。如果選擇了一個面向服務的體系結(jié)構(SOA),你必須自己動手建立大部分所需的后端,這需要大把的時間。通過LAMP你可以省下許多開銷,但是一旦你真的選擇了LAMP堆棧,類似服務的配置以及監(jiān)視將是隨之要面對的問題。隨著你對這個服務了解的加深,你必定會自費力氣做重復的工作。
正確的公司文化
建立一個可以促進生產(chǎn)的內(nèi)部環(huán)境,并根據(jù)需求不斷的進行完善。在進行正確的編碼和做出正確的產(chǎn)品之前,你首先需要定義正確的公司文化;沒有一個正確的文化,公司將不會得到發(fā)展。 #p#

七、 Instagram
利用現(xiàn)有的云基礎設施
不要去做重復的事情,你可以使用可靠并且得到證實的技術。Instagram在Amazon的EC2云計算基礎設施上運行了100多個Ubuntu 11.04實例,他們同樣還使用了Amazon ELB,其中包括3個nginx實例以及自動的故障恢復(撰稿日期)。圖片被儲存在Amazon S3上,他們還使用了Amazon CloudFront作為CDN,這么做可以有助于世界各地的圖片加載時間。
異步的任務隊列
當一個用戶決定將Instagram上的圖片分享到Twitter或者Facebook時,或者當他們需要給發(fā)布的圖片發(fā)送一個實時的通告,他們把任務推送給開源的Gearman任務管理框架。使用異步隊列意味著當“重載”在后臺進行時,媒體上傳可以快速完成。大約有200個工作者(Python編寫)忙于任務隊列的處理,處理服務中自己分割的份額。
推送通知
他們使用一個開源Apple Push Notification Service(APNS)提供程序pyapns(基于Twisted),每天穩(wěn)定地為Instagram解決10億推送消息的任務。
實時的系統(tǒng)級監(jiān)控
對于擁有100多個EC2實例的Instagram來說,對系統(tǒng)進行實時的全方位監(jiān)控無疑是重中之重。他們使用Munin進行系統(tǒng)級監(jiān)視,這個監(jiān)視工具在系統(tǒng)任何操作超過正常范圍時都會發(fā)出警報。他們開發(fā)了Munin的定制插件,基于Python-Munin之上,監(jiān)視非系統(tǒng)級事件。他們使用Pingdom進行服務的外部監(jiān)視,并且使用PagerDuty處理通知和事件。而Python的錯誤報告,他們使用Sentry,一個開源的Django應用。在任何給定的時間,他們可以實時地登錄并了解系統(tǒng)中出現(xiàn)了什么錯誤。
選擇性使用NoSQL技術(比如Redis)
Redis驅(qū)動了主feed,活動feed、會話系統(tǒng)(會話系統(tǒng)后端代碼見這里)以及其他相關系統(tǒng)。Redis所有的數(shù)據(jù)都需要寫入內(nèi)存,所以他們在EC2上為Redis運行了幾個Quadruple Extra-Large Memory實例,并且不定期給任何給定系統(tǒng)做跨Redis的分片。