新浪微博優化,可參考大併發
大家下午好,在座的大部分都是技術開發者,技術開發者往往對微博這個產品非常關心。最晚的一次,是12點多收到一個郵件說想了解一下微博底層是 怎麼構架的。很多技術人員對微博的構架非常感興趣,就是一個明星他有300萬粉絲,這個技術怎麼來實現?今天在這裡跟大家分享一下微博的底層機構,讓大家 對微博的底層技術有更好的瞭解。另外不管是做客戶端、Web 1.0、Web 2.0、論壇、部落格都要考慮架構的問題,架構實際上是有一些共性的。今天我通過講解微博裡面的一些架構,分析一下架構裡面哪些共性大家可以參考。
首先給大家介紹一下微博架構發展的歷程。新浪微博在短短一年時間內從零發展到五千萬用戶,我們的基層架構也發展了3個大的版本。第一版就 LAMP架構,優點是可以非常快的實現我們的系統。我們看一下技術特點,微博這個產品從架構上來分析,它需要解決的是發表和訂閱的問題。我們第一版採用的是推訊息模式,假如說我們一個明星使用者他有10萬個粉絲,那就是說使用者發表一條微博的時候,我們把這個微博訊息存成10萬份,這樣就是很簡單了,第一版的 架構實際上就是這兩行字。第一版的技術細節,典型的LAMP架構,是使用MyISAM搜尋引擎,它的優點就是速度非常快。
另外一個是MPSS,就是多個埠可以佈置在同一伺服器上。為什麼使用MPSS?假如說我們做一個網際網路應用,這個應用裡面有三個單元,我們可 以由2種部署方式。我們可以把三個單元分別部署在三臺伺服器上,另外一種部署模式就是這三個單元部署在每個伺服器上都有。我推薦第2種方法。這個方法解決 了兩個問題,一個是負載均衡,因為每一個單元都有多個節點處理,另外一個是可以防止單點故障。如果我們按照模式1來做的話,任何一個節點有故障就會影響我 們系統服務,如果模式二的話,任何一個結點發生故障我們的整體都不會受到影響的。
我們微博第一版上線之後,使用者非常喜歡這個產品,使用者數增長非常迅速。我們技術上碰到幾個問題。第一個問題是發表會出現延遲現象,尤其是明星使用者他的粉絲多系統需要處理很長時間。另外系統在處理明星使用者發表時系統繁忙可能會影響到其他的使用者,因為其他的使用者同一時間發表的話,也會受到這個系統的 影響。我們就考慮這個系統怎麼改進。首先是推模式,這肯定是延遲的首要原因,我們要把這個問題解決掉。其次我們的使用者越來越多,這個資料庫表從一百萬到一億,資料規模不一樣處理方式是有差別的。我們第一版單庫單表的模式,當用戶數量增多的時候,它不能滿足就需要進行拆分。第二個是鎖表的問題,我們考慮的是 更改引擎。另外一個是發表過慢,我們考慮的是非同步模式。
第二版我們進行了模組化,我們首先做了一個分層,最底層叫基礎層,首先對資料做了拆分,圖上最右邊是發表做了非同步模式。第二個服務層,我們把微博基礎的單元設計成服務層一個一個模組,最大改進是對推模式進行了改進。首先看一下投遞模式的優化,首先我們要思考推模式,如果我們做一下改進把使用者分成 有效和無效的使用者。我們一個使用者比如說有一百個粉絲,我發一條微博的時候不需要推給一百個粉絲,因為可能有50個粉絲不會馬上來看,這樣同步推送給他們, 相當於做無用功。我們把使用者分成有效和無效之後,我們把他們做一下區分,比如說當天登陸過的人我們分成有效使用者的話,只需要傳送給當天登陸過的粉絲,這樣壓力馬上就減輕了,另外投遞的延遲也減小了。
我們再看資料的拆分,資料拆分有很多方式,很多網際網路產品最常用的方法,比如說如可以按照使用者的UID來拆分。但是微博使用者的一個特點就是說大 家訪問的都是最近的資料,所以我們考慮微博的資料我們按照時間拆分,比如說一個月放一張表,這樣就解決了我們不同時間的維度可以有不同的拆分方式。第二個考慮就是要把內容和索引分開存放。假如說一條微博發表的uid,微博id是索引資料,140個字的內容是內容資料。假如我們分開的話,內容就簡單的變成了 一種key-value的方式,key-value是最容易擴充套件的一種資料。索引資料的拆分具有挑戰,比如說一個使用者發表了一千條微博,這一千條微博我們 介面前端要分頁訪問,比如說使用者需要訪問第五頁,那我們需要迅速定位到這個記錄。
假如說我們把這個索引拆分成一個月一張表,我們記錄上很難判斷第五頁在哪張表裡,我們需要載入所有的索引表。如果這個地方不能拆分,那我們系統上就會有一個非常大的瓶頸。最後我們想了一個方法,就是索引上做了一個二次索引,把每個月記錄的偏移記下來,就是一個月這個使用者發表了多少條,ID是哪 裡,就是按照這些資料迅速把記錄找出來。
非同步處理,發表是一個非常繁重的操作,它要入庫、統計索引、進入後臺,如果我們要把所有的索引都做完使用者需要前端等待很長的時間,如果有一個環節失敗的話,使用者得到的提示是發表失敗,但是入庫已經成功,這樣會帶來資料不一致問題。所以我們做了一個非同步操作,就是發表成功我們就提示成功,然後在後 臺慢慢的訊息佇列慢慢的做完。另外新浪發表了一個很重要的產品叫做MemcacheQ,我們去年做了一個對大規模部署非常有利的指令,就是statsqueue,適合大規模運維。
第二版我們做了這些改進之後,微博的使用者和訪問量並沒有停止,還有很多新的問題出現。比如說系統問題,單點故障導致的雪崩,第二個是訪問速度問題因為國內網路環境複雜,會有使用者反映說在不同地區訪問圖片、js這些速度會有問題。另外一個是資料壓力以及峰值,MySql複製延遲、慢查詢,另外就是 熱門事件,比如說世界盃,可能會導致使用者每秒發表的內容達到幾千條。我們考慮如何改進,首先系統方面允許任意模組失敗。另外靜態內容,第一步我們用CDN 來加速,另外資料的壓力以及峰值,我們需要將資料、功能、部署儘可能的拆分,然後提前進行容量規劃。
另一方面我們還有平臺化的需求,去年11月我們就說要做開放平臺,開放平臺的需求是有差異的,Web系統它有使用者行為才有請求,但是API系統特別是客戶端的應用,只要使用者一開機就會有請求,直到他關閉電腦這種請求一直會不間斷的過來,另外使用者行為很難預測。
系統規模在持續的增大,另外也有平臺化的需求,我們新架構應該怎麼做才能滿足這些需要?我們看一下同行,比如說Google怎麼樣考慮這個問題 的?Google首席科學家講過一句話,就是一個大的複雜的系統,應該要分解成很多小的服務。比如說我們在Google.com執行一個搜尋查詢的話,實 際上這個操作會調動內部一百多個服務。因此,我們第三版的考慮就是先有服務才有介面最後才有應用,我們才能把這個系統做大。
現在我們看一下第三版,首先我們把底層的東西分成基礎服務,基礎服務裡面有分散式的儲存,我們做了一些去中心化、自動化的操作。在基礎服務之上有平臺服務,我們把微博常用的應用做成各種小的服務。然後我們還有應用服務,這個是專門考慮平臺各種應用的需求。最上面我們有API,API就是新浪微博 各種第三方應用都在上面跑。
平臺服務和應用服務是分開的,這樣實現了模組隔離,即使應用服務訪問量過大的話,平臺服務不會首先影響。另外我們把微博的引擎進行了改進,實現了一個分層關係。使用者的關注關係,我們改成一個多惟度的索引結構,效能極大的提高。第四個層面就是計數器的改進,新版我們改成了基於偏移的思路,就是一個 使用者他原來讀的一個ID比如說是10000,系統最系的ID是10002的話,我們很清楚他有兩條未讀。原來的版本是採用絕對計數的,這個使用者有幾條未讀 都是用一個儲存結構的話,就容易產生一致性的問題,採用這種偏移的技術基本上不會出錯。
另外基礎服務DB冷熱分離多維度拆分,在微博裡面我們是按照時間拆分的,但是一個大型的系統裡面有很多業務需要有不同的考慮。比如說私信這個就不能按照時間來拆分,這個按照UID來拆分可能更簡單。然後我們突出儲存還做了一個去中心化,就是使用者上傳圖片的速度會極大的提高,另外察看其他使用者的圖片速度也會極大的提高。另外是動態內容支援多IDC同時更新,這個是在國內比較新穎的。
下面給大家介紹一下新浪微博怎麼樣打造一個高效能架構。到目前為止有五千萬用戶使用新浪微博,最高發表3000條以上每秒,然後一個明星使用者發 表的話,會被幾百萬使用者同時讀到。這些問題的本質是我們架構需要考慮高訪問量、海量資料的情況下三個問題。易於擴充套件、低延遲、高可用和異地分佈。我們每天有數十億次外部網頁以及API介面的需求,我們知道微博的特點是使用者請求是無法cache的。因此面對這個需求我們怎麼樣擴充套件?幾點思路。第一我們的模組設計上要去狀態,我們任意一個單元可以支援任意節點。另外是去中心化,避免單點及瓶頸。另外是可線性擴充套件。最後一個是減少模組。
我們要做一個高效能的系統,要具備一個低延遲、高實時性,微博要做到高實時性這是核心的價值,實時性的核心就是讓資料離CPU最近,避免磁碟的
IO。我們看淘寶核心系統專家餘鋒說過的一句話“CPU訪問L1就像從書桌拿一本書,L2是從書架拿一本書,L3是從客廳桌子上拿一本書,訪問 主存就像騎車去社群圖書館拿一書”。我們微博如果要做到非常實時的話,我們就需要把資料儘量離CPU節點最近。所以我們看一下cache設計裡面怎麼達到 這個目標。首先INBOX,這個資料我們需要放再一個最快的地方,因為使用者隨時訪問。OutBOX裡面的最近發表就是L1cache,還有一個是中期的, 這個因為訪問少一點,它可以被踢。最後一部分內容體有三部分。L0是本地的,我們需要把一些經常訪問的,比如說明星發表微博的內容體本地化,因為它被訪問 的概率非常大。然後L1裡面存放著最近發表的,還有一個是中期的。我們通常用L2就可以了,L1我們可以理解成它就是一個RAM儲存。
一個好的架構還需要舉行高可用性。我們看一下業界的指標,S3是99.9%,EC2是99.5%,我們另外一個同行Facebook在這方面它 是沒有承諾的,就是介面可用寫。微博平臺目前承諾的是99.95%,就是說一天365天故障率應該小於9小時。這個怎麼達到?第一我們要做容量規劃,要做 好監控以及入口的管理,就是說有些服務如果訪問量過了的話,我們要有一個開關可以攔住他。我們通過這個圖表可以清楚的看到,比如說我們要做L1的 cache,我們剩餘空間有多少,比如說80%,就說明這個資料有可能會丟失,有可能會對我們的系統造成影響。
另外一個層面就是介面監控,我們目前有Google維度的介面監控,包括訪問錯誤失敗率。然後要做架構,給大家一個很重要的經驗分享,就是說監控的指標儘量量化。比如說他延遲30秒是小問題,如果是延遲10分鐘我們就要立即採取措施了,就是所有可以量化的指標都要量化。
然後我們看監控怎麼樣更好的做?我們看亞馬遜的VP說過的一句話,就是說監控系統確實特別好,可以立即告訴我們哪裡有故障,但是有20%的概率 我們人是會出錯的。所以我們一個大型系統就應該要為自動化設計,就是說盡可能的將一些運作自動化。比如說釋出安裝、服務、啟用、停止。我們再看另外一句,Google的工程師是怎麼做的。他是這麼做的,比如說第一週是處理線上的業務,這一週他處理了很多事情,處理了很多系統的情況,剩下幾周時間沒有別的工作,他只要把這一週碰到的情況用程式的方法來解決,下次再碰到這種情況很簡單的一個按鈕就可以處理了。我們目前也在向自動化這方面努力,就是我們的工 具在持續增加。
另外一個異地分佈,在國內網路環境下,比如說IDC災難,機房檢修甚至是機房掉電,我們也碰到過中國最好的機房也會掉電,所以要每個服務單元都能支援多機房部署。另外做多機房部署有一個好處,就是使用者的訪問速度會提高。多IDC分佈靜態內容就不說了,基本上大的網際網路公司都會做,它非常成熟基本上沒有什麼問題,比如說圖片等等的靜態內容。動態內容的CDN分佈是業內的難點,國內很少有公司能夠做到非常成熟的多機房動態內容釋出的成熟方案,它的核心就是分散式儲存。一款理想的分散式儲存產品它有哪些需求呢?首先它要支援海量規模、可擴充套件、高效能、低延遲、高可用。第二個是需要多機房分佈,能夠滿足 國內負責的網路環境,還要具備異地容災能力。第三個就是要呼叫簡單,具備豐富資料庫特性。因此分散式儲存需要解決一個多對多的資料複製。
如果要做複製無非是三種策略,第一個是Master/Slave,但是它也兩個缺點,第一個是Master是中心化的,如果Master在北京 那廣州訪問就非常慢。第二個缺點是有單點風險的,比如說Master在北京,能立即遷到廣州嗎?這樣有個時間視窗的資料就丟失了,而且需要人工的干預,而 且日常廣州的使用者訪問北京的Master是有很大延遲問題的,所以一般來說要做的非常優秀是不會考慮第一種方案的。第二種就是Multi-Master方 案,它需要應用避免衝突,就是我們不能多處改變。這個對於微博來說不會特別難,我們的使用者通常只會再一個地方發表微博,使用者不會同時在廣州又在北京發表或者是修改自己的資料,這樣的話我們應用上就已經避免了這種情況。第三個就是Paxos就是可以達到強一致寫,就是一條資料如果成功肯定是多個機房都成功了,這個也顯而易見就是延遲性非常大。因此總結一下Multi-Master是最成熟的策略,但是它現在沒有成熟的產品,因為確實沒有。
我們再來看微博的方案,所以我們自己實現了一個多機房同步的方案。就是我們前端應用將資料寫到資料庫,再通過一個訊息代理,相當於通過我們自己開發的一個技術,將資料廣播到多個機房。這個不但可以做到兩個機房,而且可以做到三個、四個。具體的方式就是通過訊息廣播方式將資料多點分佈,就是說我們 的資料提交給一個代理,這個代理幫我們把這些資料同步到多個機房,那我們應用不需要關心這個資料是怎麼樣同步過去的。
用這種訊息代理方式有什麼好處呢?可以看一下Yahoo是怎麼來做的?第一個是資料提供之後沒有寫到db之後是不會消失的,我只要把資料提交成 功就可以了,不需要關心資料怎麼到達機房。第二個特點YMB是一款訊息代理的產品,但是它唯一神奇的地方是為廣域網設計的,它可以把多機房應用歸到內部, 我們應用不需要關注這個問題。這個原理跟我們目前自己開發的技術相似。
然後我們再看一下目前即將推出的微博平臺的新架構。我們知道API大部分的請求都為了獲取最新的資料。API請求有一個特點,它大目前呼叫都是空返回的,比如說一款手機的客戶端每隔一分鐘它都要呼叫伺服器一下,就是有沒有新資料,大目前的呼叫都是空返回,就是說不管伺服器有沒有資料都要呼叫一 次。這次詢問到下一次詢問中間,如果有新的資料來了,你是不會馬上知道的。因此我們想API能不能改用推的方式,就是客戶端不需要持續的呼叫,如果有新數 據就會推過去。技術特點,顯而易見低延遲,就是從發表到接受1秒內完成,實際上可能用不了1秒。然後服務端的連線就是高併發長連線服務,就是多點都連線在我們的伺服器上,這個比傳統的API要大很多。
我們看一下推送架構怎麼從架構底層做到實時性的。從左上角的一條微博在我們系統釋出之後,我們把它放在一個訊息佇列裡面,然後會有一個訊息佇列的處理程式把它拿過來,處理以後放到db裡面。假如說我們不做持久化,因為我們推送資料也不能丟失,我們就要寫一個很複雜的程式,將資料非同步去存,這樣就會非常複雜,而且系統也會有不穩定的因素。從另外一個角度來說,我們做持久化也是做過測試的。我們推送整個流程可以做到100毫秒和200毫秒之間,就是 說我們在這個時間能把資料推送出去。
我們再看一下內部細節,就是我們收到資料之後首先要經過最上面RECEIVER。然後推到我們的引擎裡面,這個引擎會做兩個事情,首先會把使用者 的關係拿過來,然後按照使用者關係馬上推送給他相應的粉絲。所以我們呼叫方已經在那兒等待了,我們需要有一個喚醒操作,就是說在介面這兒把它喚醒,然後把它傳送過去。最後是一個高併發的長連伺服器,就是一臺伺服器支援10萬以上的併發連線。最右邊中間有一個圓圈叫做Stream Buffer,我們需要Stream Buffer是要儲存使用者最近的資料。因為使用者可能會有斷線的,比如說他傳送資料的時候斷線半分鐘,我們需要把這半分鐘補給他。這就是我們的推送架構。
下面介紹一下平臺安全部分。由於我們的介面是完全開放的,所以我們要防範很多惡意行為,有很多人擔心我們介面是開放的,是不是有人通過這個介面發垃圾廣告,或者是刷粉絲,我們技術架構怎麼來防範這一點呢?這是我們的安全架構,做了三個層面的事情。最上面是我們有一個實時處理,比如說根據頻度、內 容的相似性來進行判斷,判斷髮的是不是廣告或者是垃圾內容。中間這個是一個日誌處理器,我們會根據一些行為進行判斷,比如說如果我們只是實時攔截的話,有些行為很難防止,我們做了個離線糾正的模組,比如說他潛伏的幾個月開始發廣告了,我們可以事後把這些人清除掉,以保證我們平臺的健康。最後是通過監控的維 度來保證內容的安全。目前內容安全的架構大概是541的體系,就是說我們的實時攔截可以做到50%的防止,離線分析大概可以做到40%的防止。
微博平臺需要為使用者提供安全及良好的體驗應用,以及為開發者營造一個公平的環境,所以我們的介面需要清晰安全的規則。從一個APP呼叫我們的接 口,需要幾個階層,需要劃分不同的業務模組。第二個是安全層。第三個是許可權層。這是我們平臺安全的兩個維度,一個介面安全,一個是內容安全。
我今天講的是架構方面的問題,在座大部分是開發者,可能大家都在處理不同的架構問題,架構很多地方是相通的。我們需要做一個軟體系統需要解決的本質問題是什麼?微博第一版解決釋出規模問題,第二版是解決資料規模的問題,第三版是解決服務化的問題。將複雜的問題簡單化之後,我們才可以設計出一個容 易擴充套件的大規模架構。我今天介紹就這麼多,我們微博實際上是很需要各方面的技術人員,大家對我們的架構如果感興趣的話、對我們的系統感興趣的話,也希望各方面的技術人員參與我們微博的團隊,隨時可以給我微博上發私信。