1 概述
首先給大家介紹一下新浪微博架構發展的歷程,新浪微博在短短一年時間內從零發展到五千萬用戶,我們的基層架構也發展了3個大的版本。
2 架構演變
2.1 第一版LAMP架構
第一版就 LAMP架構,優點是可以非常快的實現我們的系統。我們看一下技術特點,微博這個產品從架構上來分析,它需要解決的是發表和訂閱的問題。我們第一版採用的 是推消息模式,假如說我們一個明星用戶他有10萬個粉絲,那就是說用戶發表一條微博的時候,我們把這個微博消息存成10萬份,這樣就是很簡單了,第一版的 架構實際上就是這兩行字。
2.1.1 結構
- 微博的本質是解決發表/訂閱問題
- 第1版採用推消息模式,將發表/訂閱簡化成insert/select問題
第一版的技術細節,典型的LAMP架構,是使用MyISAM搜索引擎,它的優點就是速度非常快。
另外一個是MPSS(Multi-Port Single Server), 就是多個端口可以佈置在同一服務器上。爲什麼使用MPSS?假如說我們做一個互聯網應用,這個應用裏面有三個單元,我們可以由2種部署方式。我們可以把三 個單元分別部署在三臺服務器上,另外一種部署模式就是這三個單元部署在每個服務器上都有。我推薦第2種方法。這個方法解決了兩個問題,一個是負載均衡,因 爲每一個單 元都有多個節點處理,另外一個是可以防止單點故障。如果我們按照模式1來做的話,任何一個節點有故障就會影響我們系統服務,如果模式二的話,任何一個結點 發生故障我們的整體都不會受到影響的。
2.1.2 問題和架構演變
- 用戶快速增長
- 出現發表延遲現象,尤其是明星用戶
2.2 第二版架構
我們微博第一版上線之後,用戶非常喜歡這個產品,用戶數增長非常迅速。我們技術上碰到幾個問題。
第一個問題是發表會出現延遲現象,尤其是明星用戶他的粉絲多系統需要處理很長時間。另外系統在處理明星用戶發表時系統繁忙可能會影響到其他的用戶,因爲其他的用戶同一時間發表的話,也會受到這個系統的影響。
解決思路:
- 分發推送是造成發表延遲的主要原因
- 模式改進
- 數據規模增大也帶來一定延遲
- 規模增大:數據拆分
- 鎖表問題:更改引擎
我們就考慮這個系統怎麼改進。首先是推模式,這肯定是延遲的首要原因,我們要把這個問題解決掉。其次我們的用戶越來越多,這個數據庫表從一百萬到一億,數據 規模不一樣處理方式是有差別的。我們第一版單庫單表的模式,當用戶數量增多的時候,它不能滿足就需要進行拆分。第二個是鎖表的問題,我們考慮的是更改引 擎。另外一個是發表過慢,我們考慮的是異步模式。
第二版我們進行了模塊化:
我們首先做了一個分層,最底層叫基礎層,首先對數據做了拆分,圖上最右邊是發表做了異步模式。
2.2.1 投遞模式優化
第二個服務層,我們把微博基礎的單元設計成服務層一個一個模塊,最大改進是對推模式進行了改進。首先看一下投遞模式的優化,首先我們要思考推模式,如果我們 做一下改進把用戶分成有效和無效的用戶。我們一個用戶比如說有一百個粉絲,我發一條微博的時候不需要推給一百個粉絲,因爲可能有50個粉絲不會馬上來看, 這樣同步推送給他們,相當於做無用功。我們把用戶分成有效和無效之後,我們把他們做一下區分,比如說當天登陸過的人我們分成有效用戶的話,只需要發送給當 天登陸過的粉絲,這樣壓力馬上就減輕了,另外投遞的延遲也減小了。
總結:
- 推模式改進,不需要推送到所有用戶
- 存儲和發表峯值壓力減輕
- 投遞延遲減小
2.2.2 數據拆分
我們再看數據的拆分,數據拆分有很多方式,很多互聯網產品最常用的方法,比如說如可以按照用戶的UID來拆分。但是微博用戶的一個特點就是說大家訪問的都是最近的數據,所以我們考慮微博的數據我們按照時間拆分, 比如說一個月放一張表,這樣就解決了我們不同時間的維度可以有不同的拆分方式。第二個考慮就是要把內容和索引分開存放。假如說一條微博發表的uid, 微 博id是索引數據,140個字的內容是內容數據。假如我們分開的話,內容就簡單的變成了一種key-value的方式,key-value是最容易擴展的 一種數據。索引數據的拆分具有挑戰,比如說一個用戶發表了一千條微博,這一千條微博我們接口前端要分頁訪問,比如說用戶需要訪問第五頁,那我們需要迅速定 位到這個記錄。假如說我們把這個索引拆分成一個月一張表,我們記錄上很難判斷第五頁在 哪張表裏,我們需要加載所有的索引表。如果這個地方不能拆分,那我們系統上就會有一個非常大的瓶頸。最後我們想了一個方法,就是索引上做了一個二次索引, 把每個月記錄的偏移記下來,就是一個月這個用戶發表了多少條,ID是哪裏,就是按照這些數據迅速把記錄找出來。
總結:
- 優先按時間維度拆分
- 內容和索引分開存放
- 內容使用key-value方式存儲(NoSQL)
- 索引由於有分頁,拆分有挑戰
2.2.3 異步處理
異步處理,發表是一個非常繁重的操作,它要入庫、統計索引、進入後臺,如果我們要把所有的索引都做完用戶需要前端等待很長的時間,如果有一個環 節失敗的話,用戶得到的提示是發表失敗,但是入庫已經成功,這樣會帶來數據不一致問題。所以我們做了一個異步操作,就是發表成功我們就提示成功,然後在後 臺慢慢的消息隊列慢慢的做完。另外新浪發表了一個很重要的產品叫做MemcacheQ,我們去年做了一個對大規模部署非常有利的指令,就是 stats queue,適合大規模運維。
第二版我們做了這些改進之後,微博的用戶和訪問量並沒有停止,還有很多新的問題出現。比如說系統 問題,單點故障導致的雪崩,第二個是訪問速度問題因爲國內網絡環境複雜,會有用戶反映說在不同地區訪問圖片、js這些速度會有問題。另外一個是數據壓力以 及峯值,MySql複製延遲、慢查詢,另外就是熱門事件,比如說世界盃,可能會導致用戶每秒發表的內容達到幾千條。我們考慮如何改進,首先系統方面允許任 意模塊失敗。另外靜態內容,第一步我們用CDN 來加速,另外數據的壓力以及峯值,我們需要將數據、功能、部署儘可能的拆分,然後提前進行容量規劃。
總結:
- 發表異步化
- 發表速度和可靠性得到提高
- 使用MemcacheQ
- 增加stats queue,適合大規模運維
技術細節:
- InnoDB引進,避免鎖表煩惱
- PHP中libmemcached代替memecache
- 在高併發下穩定性極大提高
2.2.4 高速發展
- 系統問題
- 單點故障
- 訪問速度,國內複雜網絡環境
- 數據壓力和峯值
- MySQL複製延遲、慢查詢
- 熱門事件微博發表量,明星評論及粉絲
2.2.5 如何改進
- 系統方面
- 允許任意模塊失敗
- 靜態內容CDN加速
- 數據壓力和峯值
- 將數據、功能、部署儘可能拆分
2.3 第三版平臺化
2.3.1 平臺化需求
另一方面我們還有平臺化的需求,去年11月我們就說要做開放平臺,開放平臺的需求是有差異的,Web系統它有用戶行爲纔有請求,但是API系統特別是客戶端的應用,只要用戶一開機就會有請求,直到他關閉電腦這種請求一直會不間斷的過來,另外用戶行爲很難預測。
系 統規模在持續的增大,另外也有平臺化的需求,我們新架構應該怎麼做才能滿足這些需要?我們看一下同行,比如說Google怎麼樣考慮這個問題 的?Google首席科學家講過一句話,就是一個大的複雜的系統,應該要分解成很多小的服務。比如說我們在Google.com執行一個搜索查詢的話,實 際上這個操作會調動內部一百多個服務。因此,我們第三版的考慮就是先有服務纔有接口最後纔有應用,我們才能把這個系統做大。
平臺化需求:
- Web系統
- 有用戶行爲纔有請求
- API系統
- 輪詢請求
- 峯值不明顯
- 用戶行爲很難預測
2.3.2 架構如何設計
“Break large complex systems down into many services… google.com search touches 100s of service(ads, web search, books, news, spelling correction…”
--Jeff Dean, Google Fellow
2.3.3 第三版結構
服務化:
服務-〉接口-〉應用
現在我們看一下第三版:
- 首先我們把底層的東西分成基礎服務,基礎服務裏面有分佈式的存儲,我們做了一些去中心化、自動化的操作。在基礎服務之上有平臺服務,我們把微博常用的應用做成各種小的服務。
- 然後我們還有應用服務,這個是專門考慮平臺各種應用的需求。最上面我們有API,API就是新浪微博 各種第三方應用都在上面跑。
- 平臺服務和應用服務是分開的,這樣實現了模塊隔離,即使應用服務訪問量過大的話,平臺服務不會首先影響。另外我們把微博的引擎進行了改進,實現了一個分層關係。用戶的關注關係,我們改成一個多惟度的索引結構,性能極大的提高。
- 第四個層面就是計數器的改進,新版我們改成了基於偏移的思路,就是一個 用戶他原來讀的一個ID比如說是10000,系統最系的ID是10002的話,我們很清楚他有兩條未讀。原來的版本是採用絕對計數的,這個用戶有幾條未讀 都是用一個存儲結構的話,就容易產生一致性的問題,採用這種偏移的技術基本上不會出錯。
- 另外基礎服務DB冷熱分離多維度拆分,在微博裏面我們是按照時間拆分的,但是一個大型的系統裏面有很多業務需要有不同的考慮。比如說私信這個就不能按照時 間來拆分,這個按照UID來拆分可能更簡單。然後我們突出存儲還做了一個去中心化,就是用戶上傳圖片的速度會極大的提高,另外察看其他用戶的圖片速度也會 極大的提高。另外是動態內容支持多IDC同時更新,這個是在國內比較新穎的。
2.3.4 平臺服務
- 平臺服務和應用服務分開,模塊隔離
- 新微博引擎,實現feed cache分層
- 關係多維度索引結構,性能極大提高
- 計數服務改成基於偏移,更高的一致性、低延遲
2.3.5 基礎服務
- DB冷熱分離等多維度拆分
- 圖片等存儲去中心化
- 動態內容支持多IDC同時更新
3 如何打造高性能架構
高性能架構:
- 50,000,000用戶使用新浪微博
- 最高發表3000條微博/秒
- 姚晨發表一條微博,會被6,689,713粉絲讀到(11/10/2010)
下面給大家介紹一下新浪微博怎麼樣打造一個高性能架構。到目前爲止有五千萬用戶使用新浪微博,最高發表3000條以上每秒,然後一個明星用戶發表的話,會被 幾百萬用戶同時讀到。這些問題的本質是我們架構需要考慮高訪問量、海量數據的情況下三個問題。易於擴展、低延遲、高可用和異地分佈。
3.1 問題本質和思路
- 解決高訪問量、海量數據規模下:
- 易於擴展、低延遲
- 高可用
- 異地分佈能力
我們每天有數十億次外部網頁以及API接口的需求,我們知道微博的特點是用戶請求是無法cache的。因此面對這個需求我們怎麼樣擴展?幾點思路。第一我們 的模塊設計上要去狀態,我們任意一個單元可以支持任意節點。另外是去中心化,避免單點及瓶頸。另外是可線性擴展。最後一個是減少模塊。
3.1.1 思路:
- 去狀態,可請求服務單元中任意節點
- 去中心化,避免單點及瓶頸
- 可線形擴展,如
- 100萬用戶,10臺服務器
- 1000萬用戶,100臺服務器
- 減少模塊耦合
3.1.2 實時性
- 高性能系統具備低延遲、高實時性
- 實時性的核心是讓數據離CPU最近,避免磁盤IO
“CPU訪問L1就像從書桌拿一本書,L2是從書架拿一本書,L3是從客廳的桌子上拿一本書,訪問主存就像騎車去社區圖書館拿一本書”
--淘寶系統專家餘錚
3.2 IO和緩存
我們要做一個高性能的系統,要具備一個低延遲、高實時性,微博要做到高實時性這是核心的價值,實時性的核心就是讓數據離CPU最近,避免磁盤的 IO。我們看淘寶核心系統專家餘鋒說過的一句話“CPU訪問L1就像從書桌拿一本書,L2是從書架拿一本書,L3是從客廳桌子上拿一本書,訪問主存就像騎 車去社區圖書館拿一書”。我們微博如果要做到非常實時的話,我們就需要把數據儘量離CPU節點最近。所以我們看一下cache設計裏面怎麼達到這個目標。 首先INBOX,這個數據我們需要放再一個最快的地方,因爲用戶隨時訪問。OutBOX裏面的最近發表就是L1cache,還有一個是中期的,這個因爲訪 問少一點,它可以被踢。最後一部分內容體有三部分。L0是本地的,我們需要把一些經常訪問的,比如說明星發表微博的內容體本地化,因爲它被訪問的概率非常 大。然後L1裏面存放着最近發表的,還有一個是中期的。我們通常用L2就可以了,L1我們可以理解成它就是一個RAM存儲。
3.3 高可用
一個好的架構還需要舉行高可用性。我們看一下業界的指標,S3是99.9%,EC2是99.5%,我們另外一個同行Facebook在這方面它是沒有承諾 的,就是接口可用寫。微博平臺目前承諾的是99.95%,就是說一天365天故障率應該小於9小時。這個怎麼達到?第一我們要做容量規劃,要做好監控以及 入口的管理,就是說有些服務如果訪問量過了的話,我們要有一個開關可以攔住他。我們通過這個圖表可以清楚的看到,比如說我們要做L1的 cache,我們剩餘空間有多少,比如說80%,就說明這個數據有可能會丟失,有可能會對我們的系統造成影響。
l 好的架構具有高可用性
l 業界:
n Amazon S3:99.9%
n Amazon EC2:99.95%
n Facebook:n/a
如何達到高可用性:
l 容量規劃
n 圖表
l 監控
n 接口及資源監控,7X24
n 業務迴環測試,檢測業務邏輯有效性
3.3.1 接口監控
另外一個層面就是接口監控,我們目前有Google維度的接口監控,包括訪問錯誤失敗率。然後要做架構,給大家一個很重要的經驗分享,就是說監控的指標 儘量量化。比如說他延遲30秒是小問題,如果是延遲10分鐘我們就要立即採取措施了,就是所有可以量化的指標都要量化。
然後我們看監控怎麼 樣更好的做?我們看亞馬遜的VP說過的一句話,就是說監控系統確實特別好,可以立即告訴我們哪裏有故障,但是有20%的概率 我們人是會出錯的。所以我們一個大型系統就應該要爲自動化設計,就是說儘可能的將一些運作自動化。比如說發佈安裝、服務、啓用、停止。我們再看另外一 句,Google的工程師是怎麼做的。他是這麼做的,比如說第一週是處理線上的業務,這一週他處理了很多事情,處理了很多系統的情況,剩下幾周時間沒有別 的工作,他只要把這一週碰到的情況用程序的方法來解決,下次再碰到這種情況很簡單的一個按鈕就可以處理了。我們目前也在向自動化這方面努力,就是我們的工 具在持續增加。
接口監控:
l curl/各地請求情況及相應時間
l 流量異常/access log
l non-200結果/失敗率/exceptions
l 將監控指標量化
3.3.2 自動化
l 大規模互聯網系統運作需要儘可能自動化
n 發佈及安裝
n 服務啓用、停止
n 故障處理
l 前提,去狀態化,允許故障及充啓
3.3.3 分佈式
另外一個異地分佈,在國內網絡環境下,比如說IDC災難,機房檢修甚至是機房掉電,我們也碰到過中國最好的機房也會掉電,所以要每個服務單元都能支持多 機房部署。另外做多機房部署有一個好處,就是用戶的訪問速度會提高。多IDC分佈靜態內容就不說了,基本上大的互聯網公司都會做,它非常成熟基本上沒有什 麼問題,比如說圖片等等的靜態內容。動態內容的CDN分佈是業內的難點,國內很少有公司能夠做到非常成熟的多機房動態內容發佈的成熟方案,它的核心就是分 布式存儲。一款理想的分佈式存儲產品它有哪些需求呢?首先它要支持海量規模、可擴展、高性能、低延遲、高可用。第二個是需要多機房分佈,能夠滿足 國內負責的網絡環境,還要具備異地容災能力。第三個就是要調用簡單,具備豐富數據庫特性。因此分佈式存儲需要解決一個多對多的數據複製。
如 果要做複製無非是三種策略,第一個是Master/Slave,但是它也兩個缺點,第一個是Master是中心化的,如果Master在北京 那廣州訪問就非常慢。第二個缺點是有單點風險的,比如說Master在北京,能立即遷到廣州嗎?這樣有個時間窗口的數據就丟失了,而且需要人工的干預,而 且日常廣州的用戶訪問北京的Master是有很大延遲問題的,所以一般來說要做的非常優秀是不會考慮第一種方案的。第二種就是Multi-Master方 案,它需要應用避免衝突,就是我們不能多處改變。這個對於微博來說不會特別難,我們的用戶通常只會再一個地方發表微博,用戶不會同時在廣州又在北京發表或 者是修改自己的資料,這樣的話我們應用上就已經避免了這種情況。第三個就是Paxos就是可以達到強一致寫,就是一條數據如果成功肯定是多個機房都成功 了,這個也顯而易見就是延遲性非常大。因此總結一下Multi-Master是最成熟的策略,但是它現在沒有成熟的產品,因爲確實沒有。
Multi-Master:
l Web應用多地區同步的最佳策略
l 沒有現成成熟的產品
通過消息廣播方式將數據多地分佈,類似Yahoo!Message Broker
我 們再來看微博的方案,所以我們自己實現了一個多機房同步的方案。就是我們前端應用將數據寫到數據庫,再通過一個消息代理,相當於通過我們自己 開發的一個技術,將數據廣播到多個機房。這個不但可以做到兩個機房,而且可以做到三個、四個。具體的方式就是通過消息廣播方式將數據多點分佈,就是說我們 的數據提交給一個代理,這個代理幫我們把這些數據同步到多個機房,那我們應用不需要關心這個數據是怎麼樣同步過去的。
用這種消息代理方 式有什麼好處呢?可以看一下Yahoo是怎麼來做的?第一個是數據提供之後沒有寫到db之後是不會消失的,我只要把數據提交成 功就可以了,不需要關心數據怎麼到達機房。第二個特點YMB是一款消息代理的產品,但是它唯一神奇的地方是爲廣域網設計的,它可以把多機房應用歸到內部, 我們應用不需要關注這個問題。這個原理跟我們目前自己開發的技術相似。
然後我們再看一下目前即將推出的微博平臺的新架構。我們知道 API大部分的請求都爲了獲取最新的數據。API請求有一個特點,它大目前調用都是 空返回的,比如說一款手機的客戶端每隔一分鐘它都要調用服務器一下,就是有沒有新數據,大目前的調用都是空返回,就是說不管服務器有沒有數據都要調用一 次。這次詢問到下一次詢問中間,如果有新的數據來了,你是不會馬上知道的。因此我們想API能不能改用推的方式,就是客戶端不需要持續的調用,如果有新數 據就會推過去。技術特點,顯而易見低延遲,就是從發表到接受1秒內完成,實際上可能用不了1秒。然後服務端的連接就是高併發長連接服務,就是多點都連接在 我們的服務器上,這個比傳統的API要大很多。
我們看一下推送架構怎麼從架構底層做到實時性的。從左上角的一條微博在我們系統發佈之 後,我們把它放在一個消息隊列裏面,然後會有一個消息隊列 的處理程序把它拿過來,處理以後放到db裏面。假如說我們不做持久化,因爲我們推送數據也不能丟失,我們就要寫一個很複雜的程序,將數據異步去存,這樣就 會非常複雜,而且系統也會有不穩定的因素。從另外一個角度來說,我們做持久化也是做過測試的。我們推送整個流程可以做到100毫秒和200毫秒之間,就是 說我們在這個時間能把數據推送出去。
我們再看一下內部細節,就是我們收到數據之後首先要經過最上面RECEIVER。然後推到我們的引擎 裏面,這個引擎會做兩個事情,首先會把用戶的關係拿過來,然後按照用戶關係馬上推送給他相應的粉絲。所以我們調用方已經在那兒等待了,我們需要有一個喚醒 操作,就是說在接口這兒把它喚醒,然後把它 發送過去。最後是一個高併發的長連服務器,就是一臺服務器支持10萬以上的併發連接。最右邊中間有一個圓圈叫做StreamBuffer,我們需要 StreamBuffer是要保存用戶最近的數據。因爲用戶可能會有斷線的,比如說他發送數據的時候斷線半分鐘,我們需要把這半分鐘補給他。這就是我們的 推送架構。
3.4 新推送架構
3.4.1 現狀
l API大部分請求都是爲了獲取最新的數據
l 重新思考Rest API
n 大部分調用都是空返回
n 大部分時間都在處理不必要的詢問
3.4.2 如何解決
l 新一代推送接口(Stream API)
l 採用推送的方式
n 有新數據服務器立即推送給調用方
n 無數據則不消耗流量
n 客戶端實現更簡單
3.4.3 技術特點
l 低延遲,從發表到客戶端接收1秒內完成
l 高併發長連接服務器
3.4.4 推送架構
l 爲什麼先持久化
n KISS,Keep It Simple and Stupid
n 測試表明持久幾乎不增加延遲開銷
u Batch insert
u Cursor read
l Stream Buffer
n 保存用戶最近數據
n 保存客戶端斷線重連之間下行數據
3.5 平臺安全
l 由於接口開放,需要防範各類惡意行爲:
n 垃圾內容
n 垃圾粉絲
n 惡意行爲
下面介紹一下平臺安全部分。由於我們的接口是完全開放的,所以我們要防範很多惡意行爲,有很多人擔心我們接口是開放的,是不是有人通過這個接口發垃圾廣告,或者是刷粉絲,我們技術架構怎麼來防範這一點呢?
這是我們的安全架構,做了三個層面的事情。
1. 最上面是我們有一個實時處理,比如說根據頻度、內 容的相似性來進行判斷,判斷髮的是不是廣告或者是垃圾內容。
2. 中間這個是一個日誌處理器,我們會根據一些行爲進行判斷,比如說如果我們只是實時攔截的話,有 些行爲很難防止,我們做了個離線糾正的模塊,比如說他潛伏的幾個月開始發廣告了,我們可以事後把這些人清除掉,以保證我們平臺的健康。
3. 最後是通過監控的維 度來保證內容的安全。目前內容安全的架構大概是541的體系,就是說我們的實時攔截可以做到50%的防止,離線分析大概可以做到40%的防止。
微博平臺需要爲用戶提供安全及良好的體驗應用,以及爲開發者營造一個公平的環境,所以我們的接口需要清晰安全的規則。從一個APP調用我們的接 口,需要幾個階層,需要劃分不同的業務模塊。第二個是安全層。第三個是權限層。這是我們平臺安全的兩個維度,一個接口安全,一個是內容安全。
我今天講的是架構方面的問題,在座大部分是開發者,可能大家都在處理不同的架構問題,架構很多地方是相通的。我們需要做一個軟件系統需要解決的 本質問題是什麼?微博第一版解決發佈規模問題,第二版是解決數據規模的問題,第三版是解決服務化的問題。將複雜的問題簡單化之後,我們纔可以設計出一個容 易擴展的大規模架構。我今天介紹就這麼多,我們微博實際上是很需要各方面的技術人員,大家對我們的架構如果感興趣的話、對我們的系統感興趣的話,也希望各 方面的技術人員參與我們微博的團隊,隨時可以給我微博上發私信。
3.5.1 接口安全
l Auth層
n 訪問需要AppKey
n 需要OAuth授權
l 權限層
n 流量控制,權限控制