1. 程式人生 > >推薦系統---快取---人人網

推薦系統---快取---人人網

轉自: https://www.csdn.net/article/2010-07-26/277273

摘要:【Csdn.net 7月26日 獨家報道】繼成功舉辦首期TUP活動後,日前在北京麗亭華苑酒店鴻運二廳,由CSDN和《程式設計師》雜誌聯合策劃組織的TUP第二次活動如期而至,本次活動以Web 2.0技術為主題

【Csdn.net 7月26日 獨家報道】繼成功舉辦首期TUP活動後,日前在北京麗亭華苑酒店鴻運二廳,由CSDN和《程式設計師》雜誌聯合策劃組織的TUP第二次活動如期而至,本次活動以Web 2.0技術為主題,聚焦當下火熱的社交網、微博架構與實時搜尋領域。就相關領域及產品研發背後的技術、產品設計及使用者體驗話題為與會者提供全開放式的交流平臺。即使是付費沙龍,參會報名人數仍在不斷上升,本次活動有超過300人來到現場。


人人網技術經理張鐵安

以下是人人網技術經理張鐵安的演講內容:

張鐵安:
我今天跟大家分享這個內容是人人網系統架構,裡面我們會講到跟新鮮事相關的一些技術和開源一些專案,希望對大家今後工作有一些幫助。首先我要講我們新鮮事系統在SNS的主要功能。我要在人人網發一個日誌,可以很及時高效迅速的在我朋友圈、粉絲圈子裡面可以看到,我朋友可以很快回復跟我進行一個很快的互動。我必須保證系統高效運轉,同時要穩定。對於我們這樣一個SNS網站來說,包括SNS還有微博這樣一些系統,很重要一點是當發生特殊事件時會有一個爆發效應。前兩天世界盃,我不是一個足球迷,那天晚上我就睡了。兩點我手機不停的響,我說怎麼回事,我以為同事更新服務,想了想可能今天晚上是什麼比賽比較火,第二天早上說是德國隊進球了。系統遇到這種事情會有一個脈衝式的爆發,去年春節晚會趙本山小品剛開始,整個系統會非常爆炸式的報警,所以對於我們系統來說我們需要解決很多的突發事件給我們帶來的壓力,保證我們系統有足夠的穩定性。

另外要說我們這個系統裡面所有資料有很大一部分來自網站各個業務,還有一些來自於其他的入口網站,其他一些跟我們有合作關係的網站,開放平臺支援很多第三方應用或者連結他們產生實踐的時候,可以把資料發給我們FEED的系統。我們這個INPUT內容會非常複雜,我們要有很好的處理不同資料的能力。我們需要一個很好的資料規範,保證我們系統能夠接受不同型別的資料。另外一個是我們輸出包括幾個方面,一個是登陸人人首頁個人主頁列表,同時還有一個PC客戶端叫人人桌面還有手機客戶端等等。但是對於各個不同需要展示業務要求不一樣。手機展示要求我不是所有事都想要,我只想要其他一部分,會有一些選擇的需求。從各個方面我們現在這個系統設計複雜度是很高的,跟各個業務連線也是非常複雜,最終導致這個系統有一個很高的複雜度。

下面我想說一下我們這個系統面臨一些挑戰。對於人人網這樣一個網站來說,活躍使用者是非常多的,一天可能有幾千萬使用者。我們計算一下,當然這個資料可能不是一個真實的資料,我們認為每秒會產生一千條Feed、一千個客戶會產生一些內容,到系統裡面我們要處理原始資料可能是幾十億的規模。再說一下Feed的特點,當我改一個狀態我好友所有收到這些資訊就是一個擴散問題,我們需要把這個資料給這些所有想要收到資料的人看到,所以這個Feed擴散範圍很大。如果我有100個好友我要擴散到100人,如果我是一個明星就更多人會看到。

新鮮事物有這麼一個特點,我發了一篇日誌就兩個朋友看了覺得很有意思就把這個日誌分享了,如果另外一個人是那兩個人的朋友,他的頁面上有兩個一樣內容分享,這樣可能會有問題。我們會採取一種策略,把兩個相關的新鮮事合併,或者做一些替換,排序,合併這些是比較複雜。另外就是使用者請求量對人人網最大的請求量就是登陸的請求量。最後一點我剛才已經講過各個業務需求要求對新鮮事做不同的篩選。

然後講一下關於系統設計當中的兩個問題,推的模式和拉的模式。兩個模式區別在於什麼地方?推的模式意思就是說當一個事件產生的時候,我把這個事件產生時間點做N次拷貝發給他想要的人。拉是另外一種方法,當一個使用者登陸頁面的時候,首頁要顯示所有好友關注人的新鮮事。這個時候用拉的模式實現。就是說我登陸了,我查我的所有跟我有關係的列表,拿到這些列表根據這些人對應新鮮事列表裡面取所有的新鮮事再做排序,歸併的策略。推可能是非常快的操作,推過去以後,那邊立馬有了。我們登陸列表是現成,取的時候會非常快。但是有一個問題,比如說我有幾個億使用者,但是活躍使用者只有幾千萬,剩下幾個億的使用者他們可能是半年來一次,或者說一個月兩週過來一次。這些資料給他以後他可能根本沒有機會看到,這樣就浪費了很多資源。拉模式不會有這個問題,但是會有另外一個問題。你請求量很大,當用戶登陸必須很快返回資料的時候,運算量是非常大的。綜合所有考慮,因為我們要做的是一個要求實時度很高的系統,我們還是選擇推的模式,但是在用推的時候有些地方是可以做一些權衡的,把不必要系統開銷可以去掉。

這是我們現在Feed這個系統的各個層面。第一是新鮮事分發,就是說我發了一個東西以後,要把這個事情告訴所有跟我有關係的人,這個事就是頁dispatch完成的。後面有newsFeed索引的服務,跟我們新鮮事有關的東西,包括使用者的反饋,還有我們一些排序方法,跟好友關係,整個在SNS當中的朋友圈子有關係的一些東西,比如說哪些好友跟你關係很親密,你跟你老婆關係可能很親密跟他悄悄話我們都知道,還有一些你經常一起玩的朋友,你們這樣一些人的關係可能會相對比較緊密一些。我們在考慮新鮮事排序權重時我們會考慮把你老婆心情放在排序最上面,要第一時間響應領導的指示。

這個是跟我們新鮮事排序相關,包括Feed排序一些演算法,還有跟社會化網路相關的。我們正在做的基於新鮮事內容的一些興趣把內容分類,有點像百度百科,我們知道哪些使用者對音樂感興趣,哪些使用者對科技或者對政治感興趣等等。這些我們會通過一些系統計算,最後反映在新鮮事排序裡面。下面是MIINFeed就是自己發的新鮮事的列表,另外還有一個是新鮮事本身內容,我發了一個日誌新鮮事,能夠看到就是這個摘要幾十個字簡短的摘要。下面說的是我們新鮮事對於索引資料量是非常大的,我們會講一下,索引資料對我們來說有什麼意義。當我們使用者取新鮮事需要查他的索引,以後再去取內容,這個東西記憶體CACHE丟失這個使用者頁面上什麼都沒有了。所以我們要做持久化。INdexdb資料會有一個列表,寫到硬盤裡面,最後是我們渲染引擎,我們有很多的輸入和很多輸出,不同輸出要求不一樣,比如說我們給手機輸出格式和客戶端格式是完全不一樣。所以這兩個東西都是由一個系統完成。

這個是我們看到新鮮事的簡單結構圖。裡面內容不是我們現在線上系統的整個東西,可能只是其中一部分,我是把最重要的東西拿出來。一個笑臉就是一個人在上面很開心,他發了一個日誌通過我們網站日誌相關的負責日誌模組系統把這個日誌內容發新鮮事系統裡面,首先拿到就是Dis 把這個資料進行一些處理,把這個內容最終分發到三個不同的地方,第一就是newsFeed,比如說我發miniFeed有需要,第三是要把這個新鮮事產生本身內容要cache起來,會發給我們一個叢集。下面會了解我們持久化這一塊,MINIFeed量很小,我們做一個數據表就可以存下來。我們快閃記憶體100份,ID結尾為1放一起,這樣一種散表的策略分散在機器上分擔壓力。我們再說一下當一個使用者登陸人人網要取新鮮事的邏輯。如果是一個網站使用者登陸以後裝置要訪問一個伺服器,會並節一些新鮮事的內容,我們並沒有用傳統意義上的伺服器,特點就是說能夠支援很高的使用者的併發量,同時速度會非常快。我們整個網站新鮮事的WEB伺服器只有四臺,會提供一個對外的PUSH的東西,也會提供一個拉模式,網站取資料就是拉的模式。這個地方做的工作其實就是用來對新鮮事的資料和模板進行匹配,然後合併產生成TML,把資料和模板匹配在一起形成一個模組。

後面是一些技術細節的東西。第一是分發系統;第二是cache;第三是持久化儲存有渲染等等。

整個系統我們現在設計到Opnesource相關的,第一是ICE我們整個人人團隊裡面引擎這一塊使用量最大的一個通訊框架,為我們提供了一個很好的cache叢集,為我們很好的進行資料互動網路通訊方面的一些東西。其實我們很多系統是基於這個開發的,第三是memcache,所有SNS的公司如果沒用這個就不算2.0,我們也用用。我們在下層應用層之間有一層代理,有點像代理伺服器的感覺,實現了一些負載均衡策略等等。下面googleprotobuf物件的序列化及反序列化,這個東西其實可以說是非常好的,包括谷歌內部都是使用這樣一個東西,我覺得非常好。下面二進位制資料壓縮,還有多索引結構,海量儲存引擎大體就是這些東西。

下面是Feed的分發系統。使用者傳送一個新鮮事的時候傳給我們系統資料包括新鮮事資料內容,還有一些合併策略一些權重資料。一個物件是很大的,加起來可能有幾K或者幾百個位元組大小不等。首先要做的事情是要把這個資料拆一下,拆成公共用於資料再展示使用的一些文字資料,另外還有一個就是用來做排序。怎麼定位Feed這樣一個索引結構?索引結構我們系統內部INDEX架構大概一個尺寸只有32個位元組,CONTENT就很大了,這兩個資料會分別發到不同的地方,索引資料一個跟NEWSFeed另外一個給MINIFeed。我們發一條新鮮事,比如說有一個100個好友發一個日誌用推模式,發一條新鮮事,我要索引結構告訴我在我好友列表裡要追加這樣一個新鮮事的索引,要知道我好友有哪些該把這個內容發給誰,這個操作是什麼量級?一秒鐘要有一千次。我查列表有一千次,有100個好友就是100。我是一個名人,有上百萬粉絲這個是吃不消的。我們第一次查列表可以到資料庫取,第二次就要到記憶體,不能到資料庫上面查。最早的系統,大概一年多以前沒有記憶體cache,說你這個東西搞得我們天天資料庫是紅的,我們做了以後就很好了,機器基本上沒有什麼負載。第三非同步執行緒池,有的時候會有一些脈衝爆發,我們要做一些控制。當我一秒鐘有一萬次請求,有一個脈衝一下來了一萬個請求,一次給我,我可以做一個細水長流慢慢消化掉。對使用者來說朋友看到你的改的狀態是在幾秒鐘以後,不算特別遲。但是你倆又沒有在一臺電腦面前,所以他感覺不到,稍微把脈衝數據做一個平滑的曲線。對於系統的負載能力有一個很好的提升,在分發裡面對執行緒池數量是一個很重要的東西。

做講一個Feedcache的記憶體優化,講設計模式的時候,叫Flygeight。當時在書裡面一個例子,說我們在WPS做文字編輯,每一個文字有各種屬性,字型大小等等,但是一篇文章同一個字出現N次。我們把大的資料描述資料物件在全域性只有一份,我們需要使用這個字的時候,對應那個位置存一個字根,物件在一個系統裡面重複出現概率很大。這樣的操作對我們的幫助是很大的。我們曾經試圖用這個做,但是發現在效能方面有一些問題。

我們把新鮮事內容分成兩種,一種是資料內容,另外一種是索引資料。索引資料相對來說比較小,我們在另外一個cache儲存這樣一個索引,其實從巨集觀上也滿足flyweight理念。我們索引要發給100或者500人,他們拿到只是一個索引物件,真正指向內容都是同一件事。對於每一個索引cache內部我們也利用了同樣的思路,因為比如說我們散服務,我們把前站使用者放在十臺機器上面,也就是說我如果有100個好友,每臺機器上面平均算每一個服務上面有十個人的對於同一個新鮮事索引可能在每個服務裡面出現十次,做這樣一個東西我們認為一個索引結構32位元組,用最小東西指向32位元組又可以節約一些記憶體開銷。

然後我們INDEX五要支援不同業務對不同的選擇條件。有同學問記憶體怎麼建一個索引。類似一個人存資料庫資料庫支援什麼,叫一個多索引,一個數據庫表裡面可以建N個不同的索引,甚至有聯合索引,但是我們很少在記憶體裡面能夠實現這樣一個結構。如果我們自己實現可能很複雜,對於新鮮事我要按照不同緯度建立索引怎麼辦?其實提供了一個數據結構,我們可以對不同的緯度做同一個索引,對物件裡面同一個內容做更新,位元組也會自動跟著做變動。看到下面雲裡面放了四個物件,形狀不一樣,第一是按照形狀對四個物件做一個排序,第三是大小,對同一個是四個不同物件,這樣類似物件能夠支援不同的索引,我們使用它可以很方便實現多索引的結構。

關於記憶體的壓縮儲存,可以很明顯節約記憶體。右邊圖是quicklz對比圖,這個壓縮和解壓縮速度都是非常好,使用過程中我們就使用了一種方式就是把物件進行序列化,再做壓縮,在我們系統能夠節約30%-35%的記憶體。

然後講一下我們為什麼要用memcache。第一我們要支援高併發,一個使用者頁面顯示30條新鮮事,我要進行30次,把30次我想要的物件取出來再發給前端做顯示。對於人人網這麼大一個應用可能每秒PV就好幾萬,我們需要這個東西搞定記憶體的cache。還有一個就是我們資料量大,大家也知道現在伺服器的記憶體也是越來越大,原先剛到公司我們用的是16G的記憶體覺得已經比我們PC機大很多,再過一年,變成32、36,現在伺服器搞到72G。我們要做記憶體的cache,對資料查詢要求,隨著記憶體裡面cache內容不斷增加,我們要保證查詢效能不斷增強。我們保證相對在我們資料量不斷增加時查詢效能有些下降但是不能特別大。另外一個,當我們cache不可能放在一臺機器上面,當一些伺服器被重啟,我們需要cache量更大,要加一些機器進來。我們要保證整個cache能夠有擴容性,同時可以很方便摘掉一些機器, 我們需要所有cache互相之間有一些冗餘。最後我希望我們cache策略、機器足夠多,我們現在有十幾,二十幾cache伺服器,當我們做到上百臺,幾百臺機器的cache時,我們需要保證對於所有的cache伺服器管理更加的方便,不是說要重新部署一次。我們是跟FACEBOOK學的,想做這樣一個東西,我講一下自己開發的東西只是MEMcache的PROXY,做這個開源專案有兩個,但是這兩個專案我們調研了一下不是特別理想,另外有一個動力讓我們自己做這個東西的原因是因為我之前是做客戶端伺服器,對這種通訊等等東西還是比較有信心,另外一個就是說mbmcache協議是很簡單的,所以我覺得這個東西我們有把握做好,我們就做了一下,結果做成了。

基本的一個功能是什麼,就是說在這個層面上我們把所有的cache的管理都放在上面,包括策略也放在上面,我們有一個cacheLOADER。我們新鮮事操作都是PV6,到資料庫裡面查也是ID等於什麼,這樣的話我做一個cacheloader可以很好跟memcache做配合,比如說我不做新鮮事,我要加東西的時候只需要在cacheloaler做一個配製。這樣的話避免了開發人員重複開發一些用於載入的服務。另外一個就是為什麼要有關cacheporxy,因為如果沒有這些,我們跟所有散服務必須放在客戶端上面,這個事情會給開發使用這個叢集的人帶來很多不方便,隨著我們客戶端不斷增加,如果其他的業務不斷增加,使用這個叢集的人越來越多,會帶來相同困擾,有這麼一層我們就可以保證這個問題。

下面一個進索引持久化系統。為什麼要做這個東西,是因為我們在一年以前,還沒有這個東西的時候,當時經常會有一些問題。新鮮事有一些大改動,要把我們索引cache重啟,但是我這些cache在資料庫裡面是沒有辦法存的,因為這個量很大,我們剛才說每天如果我們產生的總的新鮮事量是千級萬級以上的量,平均每個人有100個好友,其實總的一天產生新鮮事索引在幾十億規模。我們想把這些索引都存下來大概需要多少臺機器?可能需要上百臺機器。所以如果一秒鐘處理十幾萬,或者幾十萬至少也要100臺以上的機器,我們必須解決這個問題。另外我們不解決這個問題怎麼做,記憶體索引cache沒有的情況之下,我們需要把原先所有使用者產生的這些新鮮事的過程從頭到尾再放一次。

剛才說傳說中的解決方案,MYSPL是不欣,APENSOURCE還是不夠快,第三就是說GFS可能解決這個問題。但是我們這個系統買不到。我們做這個的時候,我們做了一些調研,這個裡面包括新浪支援,還有百度支援的。大致上我們需要在每秒鐘解決十萬次。第三就是我們所有的每天產生的索引資料總量每天100G以上,解決的思路是什麼?第一就是普通機器我們隨即讀寫訪問就是IOPS也就能道800+的量。既然硬碟只能這樣,我們怎麼解決?據盤讀取資料是一塊一塊讀,我們索引很小,一個索引大小改動,我們會浪費很多寫的資源,我們必須要把隨即大量隨即寫變成一種順序寫檔案,我們就要把這種所有的隨機的東西變成一種順序的問題,如果能夠變成順序的東西,我們用普通的機器可以搞定這個問題。

另外一個就是說如果我們要做這個事情的話,要做一些比如說IO五方面的東西,在使用的時候用了非同步IO馬行,直接操作硬碟,使用的時候我們也跟英特爾做調研,選擇他們SSD提高硬碟寫的效能和讀的效能。

我們必須要把所有寫合併,把大量隨機變成順序寫操作,既然需要做合併,肯定我們需要先把所有的隨機索引的寫操作放在記憶體當中做一些滯後,整合以後再做寫讀。我們會把所有的寫讀操作通過LOG檔案,把LOG記錄下來,機器宕機我們可以通過回放把這些資料讀出來,我們使用TT儲存索引,為什麼很快?因為他所有資料跑一遍都在記憶體裡面,所以跟記憶體操作是一樣。我們使用TT做了一個東西,TT支援儲存方式比較簡單,作資料節點上面IO模型我們選擇非同步IO。為什麼用direct為IO遮蔽OS五的cache策略,最後使用SSD解決大量的併發讀取。

這個是整個系統節點,nidexNode責任儲存userid到最信一塊data,block的位置資訊,我們把5億使用者的使用者ID到索引塊資訊都放在記憶體,也用不到10G,用TT保證系統裡面所有檔案至少全部在記憶體裡面放下。我們用32G機器放這個檔案。另外TT實現的時候用的是共享記憶體實現方式。只要機器不死,節點服務被我殺掉,作業系統還在,記憶體還在,系統會把資料刷回硬碟。下面是DATAFILE,這就是DATAFILE的結構,左邊是FILE1,右邊是FILE2。
最後講一下模板渲染。說到資料格式的一致性,我們現在新鮮事資料格式是用Feed的輸入很多來自各個不同業務,必須保證資料格式的一字形,輸出時,通過渲染引擎將資料變化為不同的VIEW,提供給各業務。技術方案Ctemplate提供高效的模板選擇能力,還有谷歌的方式。

我今天講了很多,大家想要有深入的瞭解歡迎大家加入我們的團隊,謝謝。

提問:

剛才PPT很快過了,一些常見資料庫,mebcacheDB為什麼不要了?

張鐵安:我們對這個東西瞭解不夠多,我們寫了資料200多個動不了,我們也不知道是什麼問題。我們覺得不是特別可靠,因為這個東西上了以後必須保證不出問題,出了問題必須要知道怎麼解決。

提問:你說光良有幾百萬粉絲,我們選擇推模式,我們要把幾百萬粉絲裡面每一個粉絲把資訊推送到他們上面去,快速獲得這些粉絲資訊,粉絲資訊是放在記憶體裡面。所以我想了解,如果是我這幾百個放在記憶體是一個方式,但是幾百個粉絲是怎麼組織的?

張鐵安:就是一個列表,我們傳送會有一個表示式,我們放的時候不是說把所有放在裡面,我們其實只做了一個幾萬的佇列。為什麼這麼做?有這麼幾個目的,我們只對好友新鮮事,對於粉絲有幾百萬,這個列表實時有人加進來,這種情況下沒有辦法做一個像好友準確策略。我們做一個佇列,不要太長,我做一個幾萬,使用者登陸行為是這樣,上人人網以後在這個網站玩就是幾十分鐘就會關掉,關掉以後cache就沒有多大意義,所以逐出資料比較多。

提問:
你說現在我把使用者放到記憶體裡面,查詢索引是通過ID查。我們資料庫裡面是通過ID把這個ID和索引放到cache裡面。

張鐵安:我們兩個記憶體cache是用ICE做的。

提問:就是說這裡面有一個像硬查詢,很多裡面都是ID,把這個硬查詢也拆借出來。

張鐵安:我們memcache機器再多也不可能把所有新鮮事放到裡面,我們現在要取一千個列表,會有不到一千個列表MIS有一個長尾效應,大部分熱點資料要進行cache,對時間特別長的以前資料是不需要cache的。