瘋狂代碼,大型網站架構系列 轉貼

瘋狂代碼,大型網站架構系列之一,前言,不得不考慮的問題

來源: (www.admin5.comwww.crazycoder.cn同步發佈,轉載請註明出處)
發佈時間:星期二, 2008年9月16日 瀏覽:6976次 評論:0
<script src="Counter/ArticleCounter.js" type="text/javascript"></script>

 

前言:這兩天機器壞了,正在送修中,寫個系列的大型網站架構的文章,希望對有志在互聯網做出一番事業的站長朋友們一些幫助。
 
注意:這裏的大型網站架構只包括高互動性高交互性的數據型大型網站,基於大家衆所周知的原因,我們就不談新聞類和一些依靠HTML靜態化就可以實現的架構了,我們以高負載高數據交換高數據流動性的網站爲例,比如海內,開心網等類似的web2.0系列架構。我們這裏不討論是PHP還是JSP或者.NET環境,我們從架構的方面去看問題,實現語言方面並不是問題,語言的優勢在於實現而不是好壞,不論你選擇任何語言,架構都是必須要面對的。
 
文入正題:
首先討論一下大型網站需要注意和考慮的問題
A.      海量數據的處理。
衆所周知,對於一些相對小的站點來說,數據量並不是很大,select和update就可以解決我們面對的問題,本身負載量不是很大,最多再加幾個索引就可以搞定。對於大型網站,每天的數據量可能就上百萬,如果一個設計不好的多對多關係,在前期是沒有任何問題的,但是隨着用戶的增長,數據量會是幾何級的增長的。在這個時候我們對於一個表的select和update的時候(還不說多表聯合查詢)的成本的非常高的。
B.      數據併發的處理
在一些時候,2.0的CTO都有個尚方寶劍,就是緩存。對於緩存,在高併發高處理的時候也是個大問題。在整個應用程序下,緩存是全局共享的,然而在我們進行修改的時候就,如果兩個或者多個請求同時對緩存有更新的要求的情況下,應用程序會直接的死掉。這個時候,就需要一個好的數據併發處理策略以及緩存策略。
另外,就是數據庫的死鎖問題,也許平時我們感覺不到,死鎖在高併發的情況下的出現的概率是非常高的,磁盤緩存就是一個大問題。
C.      文件存貯的問題
對於一些支持文件上傳的2.0的站點,在慶幸硬盤容量越來越大的時候我們更多的應該考慮的是文件應該如何被存儲並且被有效的索引。常見的方案是對文件按照日期和類型進行存貯。但是當文件量 是海量的數據的情況下,如果一塊硬盤存貯了500個G的瑣碎文件,那麼維護的時候和使用的時候磁盤的Io就是一個巨大的問題,哪怕你的帶寬足夠,但是你的磁盤也未必響應過來。如果這個時候還涉及上傳,磁盤很容易就over了。
也許用raid和專用存貯服務器能解決眼下的問題,但是還有個問題就是各地的訪問問題,也許我們的服務器在北京,可能在雲南或者新疆的訪問速度如何解決?如果做分佈式,那麼我們的文件索引以及架構該如何規劃。
所以我們不得不承認,文件存貯是個很不容易的問題
D.     數據關係的處理
我們可以很容易的規劃出一個符合第三範式的數據庫,裏面佈滿了多對多關係,還能用GUID來替換INDENTIFY COLUMN 但是,多對多關係充斥的2.0時代,第三範式是第一個應該被拋棄的。必須有效的把多表聯合查詢降到最低。
E.      數據索引的問題
衆所周知,索引是提高數據庫效率查詢的最方面最廉價最容易實現的方案。但是,在高UPDATE的情況下,update和delete付出的成本會高的無法想想,筆者遇到過一個情況,在更新一個聚焦索引的時候需要10分鐘來完成,那麼對於站點來說,這些基本上是不可忍受的。
索引和更新是一對天生的冤家,問題A,D,E這些是我們在做架構的時候不得不考慮的問題,並且也可能是花費時間最多的問題,
F.      分佈式處理
對於2.0網站由於其高互動性,CDN實現的效果基本上爲0,內容是實時更新的,我們常規的處理。爲了保證各地的訪問速度,我們就需要面對一個絕大的問題,就是如何有效的實現數據同步和更新,實現各地服務器的實時通訊有是一個不得不需要考慮的問題。
G.     Ajax的利弊分析
成也AJAX,敗也AJAX,AJAX成爲了主流趨勢,突然發現基於XMLHTTP的post和get是如此的容易。客戶端get或者post到服務器數據,服務器接到數據請求之後返回來,這是一個很正常的AJAX請求。但是在AJAX處理的時候,如果我們使用一個抓包工具的話,對數據返回和處理是一目瞭然。對於一些計算量大的AJAX請求的話,我們可以構造一個發包機,很容易就可以把一個webserver幹掉。
H.      數據安全性的分析
對於HTTP協議來說,數據包都是明文傳輸的,也許我們可以說我們可以用加密啊,但是對於G問題來說的話,加密的過程就可能是明文了(比如我們知道的QQ,可以很容易的判斷他的加密,並有效的寫一個跟他一樣的加密和解密方法出來的)。當你站點流量不是很大的時候沒有人會在乎你,但是當你流量上來之後,那麼所謂的外掛,所謂的羣發就會接踵而來(從qq一開始的羣發可見端倪)。也許我們可以很的意的說,我們可以採用更高級別的判斷甚至HTTPS來實現,注意,當你做這些處理的時候付出的將是海量的database,io以及CPU的成本。對於一些羣發,基本上是不可能的。筆者已經可以實現對於百度空間和qq空間的羣發了。大家願意試試,實際上並不是很難。
I.       數據同步和集羣的處理的問題
當我們的一臺databaseserver不堪重負的時候,這個時候我們就需要做基於數據庫的負載和集羣了。而這個時候可能是最讓人困擾的的問題了,數據基於網絡傳輸根據數據庫的設計的不同,數據延遲是很可怕的問題,也是不可避免的問題,這樣的話,我們就需要通過另外的手段來保證在這延遲的幾秒或者更長的幾分鐘時間內,實現有效的交互。比如數據散列,分割,內容處理等等問題
K.數據共享的渠道以及OPENAPI趨勢
   Openapi已經成爲一個不可避免的趨勢,從google,facebook,myspace到海內校內,都在考慮這個問題,它可以更有效的留住用戶並激發用戶的更多的興趣以及讓更多的人幫助你做最有效的開發。這個時候一個有效的數據共享平臺,數據開放平臺就成爲必不可少的途徑了,而在開放的接口的情況保證數據的安全性和性能,又是一個我們必須要認真思考的問題了。
 
當然還有更多需要考慮的問題,我這裏就寫一個最需要考慮的問題,歡迎補充。
 

瘋狂代碼,大型網站架構系列之二,底層架構概論

上篇瘋狂代碼介紹的基於AJAX的攻擊很多人提出疑問,比如不能跨域,減輕負擔之類。Ajax是通過簡單的GET和POST進行數據傳遞的,採用HTTPDEBUGGER,抓取數據,然後採用如下方案,順便寫個示例的攻擊代碼.比傳統的webform,我們更容易構造一些,其實對於webform和ajax的處理和發包過程是一樣的,ajax數據量相對小,速度也快一些。
結合SharpPcap和HttpWebRequest我們構造一個合理的正常的IP數據包過去,代碼很長,我們用僞代碼簡單的表達一下。
request.CreateUrl(Ajax處理頁面);
request.Method=”GetORPost”;
request.refere=”網頁來源”;
SharpPcap.SetLinkConnection(僞造IP地址);
String content = request.GetResponseStream() 如果作爲一個多線程的應用程序對對方的WEB構成批量發包的話(假如是DEDECMS),足可以把Dedecms的數據庫搞垮
 
文入正題:
對於上回書提到要解決問題A,我們先講解一下電信公司ADSL的佈局方案。機器上沒有裝VISIO,我簡單的用文字描述一下流程。
Adsl用戶Aè輸入用戶名密碼è遠程連接到賬戶數據庫(在天津)è賬戶數據庫連接計費數據庫並返回狀è如果成功,連接PPPOE服務器,並進一步連接計費數據庫è認證服務並連接。
 
這裏沒有什麼特別的地方,但是和qq通訊服務是一樣的,就是採用了統一的用戶驗證服務器,同時對於用戶驗證的信息數據庫是隻讀的,我們從其中可以想到什麼嗎?
 
以上是個簡單的例子,下面開始談具體的架構策略,首先對於上篇提到的問題A,我們首先以用戶數據庫爲例來做解釋和要求。
首先做用戶量估算需求,假如我們做的是學術社區,那麼這個用戶量不會很大,可能我們不需要考慮這個,對於用戶量的級別,我們暫時把用戶量級別定爲三種,百萬級別(M)和千萬界別(S),以及億萬級別(Q),並考慮用戶登錄驗證以及查詢常用的操作,對M和S進行擴充以及瞭解。
 
衆所周知,在這個情況下,對於用戶數據的負載其實並非可行而不可行的問題,而是如何最大化的保證查詢和更新以及各個服務器之間的數據同步。這裏我們不再講解如何優化如何索引,只介紹架構初期的方案,下面介紹的方案如果涉及全表查詢,可以採用分區視圖的方案,大家可以具體搜索相關資料。
 
對於M級別來說,現有的DBMS經過合理的佈局完全可以滿足需求。我們需要解決的問題的關鍵其實是處理IO方面的問題,處理方案相對簡單一些,對數據庫的FILE文件分磁盤存貯(不是分區,是不同的硬盤),根據負載量大小,我們可以適當的控制硬盤的數量和文件分區的數量。
 
對於S級別,上個處理方案已經不能完全滿足需求了,這個時候我們需要對註冊和入庫的流程進行簡單的修改了,解決方案是數據散列和分區視圖的概念,具體概念大家去google一下,我不詳細說明了。
我們常用的方案有三種。第一種是等容擴充法,在用戶註冊控制的基礎上,保證每個庫的用戶容量不超過500萬,超過之後入第二個庫,以此類推,這個方案可以保證系統有效的擴充性,但不能保證數據被有效的索引。第二種就是共區索引方案,其實和第一種方案有異曲同工的之說但是講第一種方案進行了合理的優化,按照用戶名進行分庫存貯。比如我們可以建立26的數據庫,按照用戶名的索引來控制用戶數據入哪個庫。假如用戶名是CrazyCoder,那麼就講該用戶名的數據存放在用戶表C中,在數據存貯的時候可以很方便的根據用戶名進行相應的數據查詢,方案二可以有效的解決數據索引問題。方案三是一個更具模型化的方案,結合方案一和方案二,進行用戶ID的編碼,不是INDENTIFY Cloumn,我們用一種序列化的方案將用戶名以編碼的形式存貯,比如用戶名是CrazyCoder,我們的編碼方案就是通過算法進行數字化,將CrazyCoder按照C,R,A,….存貯爲數字索引,然後進行分區存貯,數字類型的數據在數據庫中可以更有效的被查詢和被更新和共享,結合方案一和方案二這個就是方案三。
 
 
對於Q級別。數據量已經是足夠海量了,這個時候無論用哪種方案都是一個讓人頭大的數據,不能簡單的用查詢的方案來處理了,可以參考S級別的進行處理。但這個時候我們採用的方案是根據用戶活躍度的權值結合數據量進行臨時數據表的存放。如果一個非意外的數據情況下,每天登錄的用戶量不會上千萬。這個時候我們需要做的是一個簡單的數據代理程序。一個臨時的用戶驗證數據庫,每天執行一次批處理,將活躍度高的用戶賬戶提取到臨時數據庫中,查詢的時候先查詢臨時庫,如果沒有在進行全庫查詢。這個根據系統的負載情況來估計閾值,不同的系統估算方案也不盡相同。
 
 
上面對於,M,S,Q三種界別進行了簡單的概述,下面介紹一個在其之上更高級的一個查詢方案,數據緩存服務器,我們也可以把它理解爲緩衝服務器,數據做爲只讀來使用。
 
具體實現方案如下:以爲涉及了海量,DBMS常規的緩存方案已經不符合我們的要求了,那麼我們需要一個有效的緩存方案,這個時候處理的流程其實就是講最常用最直接的數據直接存放在緩存服務器中,而這個緩存服務器定時從主服務器獲取並更新信息。這個是一個簡單的查詢,我們還可以更深入的講緩存服務器做二次緩存,也就是一次性處理輸入並存放到LIST數據中,作爲全局變量放到內存中進行查詢,同時用HASHTABLE或者數組進行數據組索引(可以是多級),根據查詢分佈到各個變量中。直接從內存中讀取數據。
 
以筆者的經驗來說的話,對於ITEM數據不超過10K的來說,每個列表最佳的存放範圍是0到6萬之間。
 
這裏簡單的介紹了一下DBMS基本架構,裏面具體細節處理的還有很多,這裏只介紹個大概的綱要。有問題請給我發郵件(Heroqst # Gmail.com),請講#替換爲@
 
 
這裏只是簡單的介紹了一下DBMS的基本佈局,下章講具體對我們常見的多對多關係數據庫進行具體配置說明。
 
首先介紹一下問題的大概,比如對於文章和標籤,每個文章可以有多個標籤,而每個標籤下又會有多個文章,那麼數據量將是文章數乘以標籤數,這個時候如何進行處理並有效的索引,將是下章要介紹的內容。
 

瘋狂代碼,大型網站架構系列之三,多對多關係的優化設計

上篇以用戶數據表爲例介紹了基本的數據分割方案以及基本的配置方案。但是在2.0時代,這種簡單的列表索引已經遠遠實現起來是問題的,多對多關係將是最常見的關係。現在我們針對web2.0數據中廣泛存在的多對多關係進行闡述和具體行爲判斷,比如一個很簡單的例子,在2.0時代,好友功能是最常被用到的,每個用戶會有很多的好友,同時也會是很多人的好友,那麼這個數據量將會是用戶數的平方的級別。同樣,對於文章標籤,每個文章可以有多個標籤,而每個標籤又可以有多個文章,這又是一個幾何乘積,數據量又會是個天文數字。
 
傳統的處理方案有兩種,一種是通過SEARCH的方法來實現,一種是通過另建一個索引表,存貯對應的ID以進行存貯。對於第一種方案,因爲要涉及大量的LIKE查詢,性能不敢恭維,第二種的情況下,數據庫的行的數量也是驚人海量級別的,並且要跨表跨區查詢,還要維護數據的唯一性,數據處理過程相當的複雜性能也就不言而喻了。
 
文入正題,下面對數據多對多關係舉出來具體的解決方案,我們這裏以標籤和文章之間的多對多關係爲例來講解,大家可以舉一反三的思考羣組和用戶之間,相冊和被圈用戶之間等等複雜的多對多關係。
 
首先濾清一下流程,我們以傳統方案的第二種爲例,在傳統的數據庫設計中我們是如下走的:當一篇博文發佈的時候並插入標籤的時候一般是三步走(也可以理解爲四步,以爲還要判斷標籤是否存在的問題),第一步插入文章數據庫並獲取文章的ID,第二步插入標籤數據庫同時查詢標籤是否存在,如果存在就取出標籤的ID,否則的話插入新標籤並取出ID,第三部,將文章的ID和標籤的ID插入索引表來建立關聯。如果這個時候在索引表上建立了索引的話就是災難性的,特別是在數據量大的情況下,儘管它可以有效的提高查詢速度,但是發佈的速度可能就會讓人無法忍受了。
 
我們處理的方法也是三部曲,對多對多關係進行進一步的處理。
用標籤的時候,我們用的最多的就是查詢標籤下的文章和顯示文章的標籤,所以我們實現這例就成了。
第一步,拋棄索引表。
對文章做冗餘字段,加一個TAG列,我們可以講TAG的標籤如下寫[TagID,TagName]| [TagID,TagName]| [TagID,TagName] 同樣 對於TAG表,我們做如下冗餘加個Article字段,如下內容[ArticleID,Title]| [ArticleID, Title]| [ArticleID, Title],在需要增加的時候我們只要APPEND一下就可以了,至於ARTICLE的結構和TAG的結構可以參考我上一篇文章的介紹。其實根據需要還可以存貯更多。
有人會問,爲什麼要存貯TagName和ArticleTitle呢,其實是爲了避免跨表查詢和INNERJOIN查詢來做的,In查詢和跨表查詢會造成全表遍歷,所以我們在執行的時候In查詢是必須要找到一個有效的替代方法的。
 
第二部:異步加載。
在設計模式下我們常思考的是單件模式,我們採用另類的單件模式來處理,也就是把文章和標籤之間的索引作爲專門的進程來做,異步的實現。
爲了避免文章在發佈的時候以爲要檢查TAG表而造成的線程擁堵,我們需要採取延遲加載的方案來做。服務器應該維護一個進程專業的對標籤和文章地段的查詢和索引,我們在發佈文章的時候應該把標籤同步這一塊託管給另外的一個程序進行處理,並進行索引。
第三部:標籤緩存索引:
對於頻繁的判斷標籤去或者熱門的標籤我們還可以組織一套有效的索引,比如對於標籤“瘋狂代碼”和”傲博知識庫”,我們用樹來把它表示出來。對於瘋狂代碼我們索引一個瘋,其實用程序表達就是瘋狂代碼[0],同樣傲博知識庫就是傲博知識庫[0]。而在數組”瘋”中存貯以瘋開頭的標籤組,以”傲”的數組中存貯以”傲”開頭的標籤。如果量更大的話還可以再做二級索引。
 
這涉及另外一個話題了就是分詞,上面是一個簡單的分詞方案,大家在進行GOOGLE搜索的時候應該很輸入它的Suggest方法吧,就是這個道理。最終講標籤有效的索引,並提取熱門的作爲一個全局靜態變量,我們就可以繞過數據查詢這一關,對第二部的單件模式又是一個進化。
 
以上是對多對多關係的一個簡單的架構說明,肯定有人會問,如果這樣做的話工作量不是太大了嗎,分詞處理什麼的,對每個多對多關係進行處理。
OK,咱們可以進一步的把它來抽象化,我們用TableA 表示Article表,用TagbleT表示Tag表,我們可以講字段抽象化出來,也就是一個ID,一個Tag的String 同理對於標籤表也是如此。朋友們應該可以理解我的意思了。
 
對,就是做個代碼生成器把對應的多對多關係給生成出來,這個很好寫的,幾個Append就可以搞定。如果想更方便的處理,那麼把這個東西做成單件的模式抽象化出來,然後再違反一下原則,做成基類,其他關係繼承這個基類。。。。。剩下的應該很簡單了,具體實現大家思考吧。
 
請參照第二篇的文章進行進一步優化設計來實現更高的負載性能
 
下章接着講述數據分割和散列方面的內容
 
瘋狂代碼,大型網站架構系列之四,多對多關係的以及併發緩存的設計
上篇以用戶數據表爲例介紹了基本的數據分割方案以及基本的配置方案。但是在2.0時代,這種簡單的列表索引已經遠遠實現起來是問題的,多對多關係將 是最常見的關係。現在我們針對web2.0數據中廣泛存在的多對多關係進行闡述和具體行爲判斷,比如一個很簡單的例子,在2.0時代,好友功能是最常被用 到的,每個用戶會有很多的好友,同時也會是很多人的好友,那麼這個數據量將會是用戶數的平方的級別。同樣,對於文章標籤,每個文章可以有多個標籤,而每個 標籤又可以有多個文章,這又是一個幾何乘積,數據量又會是個天文數字。這裏不再介紹基於硬件,IO,集羣方面的問題,我們以項目開發的角度來實現他
 
這裏先介紹一個基本的施行方案,而後我們進一步的對它進行擴充以滿足我們的以後的具體需求
 
對於多對多關係,傳統的處理方案有三種,一種是通過SEARCH的方法來實現,第二一種是通過另建一個索引表,存貯對應的ID以進行存貯,第三種是通過二次歸檔緩衝來實現(本人不知道用什麼語言來描述這種處理方法,姑且如此吧)
 
對於第一種方案,因爲要涉 及大量的LIKE查詢,性能不敢恭維,基於全文索引的方式可能解決這個問題,但是利用第三方的數據可能未必能適合我們的胃口,我們也可能沒有足夠的時間和精力來獨立開發實現。第二種的情況下,數據庫的行的數量也是驚人海量級別的,維護索引表的散列處理,並且要跨表跨區查詢,還要維護數據的唯一性,數據處理過程相 當的複雜性能也就不言而喻了。
 
文入正題,下面以一個簡單的例子解釋下第三種方案,對數據多對多關係舉出來具體的解決方案,我們這裏以標籤和文章之間的多對多關係爲例來講解,大家可以舉一反三的思考羣組和用戶之間,相冊和被圈用戶之間等等複雜的多對多關係,如下方案可能不是最好的方案,但是實踐證明還是綜合時間和開發成本是最合理的。
 
首先濾清一下流程,在傳統的數據庫設計中我們是如下走的:當一篇博文發佈的時候並插入標籤的時候一般是三步走(也 可以理解爲四步,以爲還要判斷標籤是否存在的問題),第一步插入文章數據庫並獲取文章的ID,第二步插入標籤數據庫同時查詢標籤是否存在,如果存在就取出 標籤的ID,否則的話插入新標籤並取出ID,第三部,將文章的ID和標籤的ID插入索引表來建立關聯。如果這個時候在索引表上建立了索引的話就是災難性 的,特別是在數據量大的情況下,儘管它可以有效的提高查詢速度,但是發佈的速度可能就會讓人無法忍受了。
 
我們處理的方法也是四部曲,對多對多關係進行進一步的處理。
用標籤的時候,我們用的最多的就是查詢標籤下的文章和顯示文章的標籤,所以我們實現這例就成了。
第一步,數據冗餘
老生常談的話題,對文章做冗餘,加一個TAG列,我們可以講TAG的標籤如下寫[TagID,TagName]| [TagID,TagName]| [TagID,TagName] 同樣 對於TAG表,我們做如下冗餘加個Article字段,如下內容[ArticleID,Title]| [ArticleID, Title]| [ArticleID, Title],在需要增加的時候我們只要APPEND一下就可以了,至於ARTICLE的結構和TAG的結構可以參考我上一篇文章的介紹。其實根據需要還 可以存貯更多。
有人會問,爲什麼要存貯TagName和ArticleTitle呢,其實是爲了避免跨表查詢和INNERJOIN查詢來做的,In查詢和跨表查詢會造成全表遍歷,所以我們在執行的時候In查詢是必須要找到一個有效的替代方法的。關於數據冗餘的問題,我們可能還會做的更變態一些,這個後面慢慢說。
 
第二步:異步存貯。
在設計模式下我們常思考的是單件模式,我們採用另類的單件模式思維來處理,也就是把文章和標籤之間的索引作爲專門的進程來做,異步的實現。
爲了避免文章在發佈的時候以爲要檢查TAG表而造成的線程擁堵,我們需要採取延遲加載的方案來做。服務器應該維護一個進程專業的對標籤和文章地段的查詢和索引,我們在發佈文章的時候應該把標籤同步這一塊託管給另外的一個進程或者服務器進行處理,並進行索引。
 
第三步:二次索引:
對於頻繁的判斷標籤去或者熱門的標籤我們還可以在內存裏組織一套有效的索引,比如對於標籤“瘋狂代碼”,我們用樹來把它表示出來。對於 瘋狂代碼我們索引一個瘋,其實用程序表達就是瘋狂代碼[0]。而在數組”瘋”中存貯以瘋開頭的標籤組,以”傲”的數 組中存貯以”傲”開頭的標籤。如果量更大的話還可以再做N級索引,將這些常用的標籤對應設計內存索引,我們可以把它想象的理解爲內存中的Suggest(比如google搜索時的Suggest),使用中我們可以直接拿來使用
第四步:針對跨表查詢的處理
很多情況下,我們可能避免不了多表查詢,或者IN,or查詢,除去業務層封裝的分區視圖集羣之外,我們還可以處理的更好,在很多情況下,我們的查詢會是非常頻繁非常統一的(這裏的統一指熱門查詢),比如在SNS中常見的性別,嗜好等多條件搜索,而這些數據可能存貯在多個數據表結構中,而這樣會吧不可避免的會產生全表遍歷查詢。
處理方法也很簡單,把原來散列的垂直分割的表再合併起來,合併到另外的只讀的訂閱服務器上,然後做適當的結構優化和索引,剩下的大家應該明白我的意思了,雖然簡單,但是這種處理方法非常適合以後服務器的橫向擴充。
 
以上是對多對多關係和多表查詢的一個簡單的架構說明,肯定有人會問,如果這樣做的話工作量不是太大了嗎,分詞處理什麼的,對每個多對多關係進行處理。
OK,咱們可以進一步的把它來抽象化,我們用TableA 表示Article表,用TagbleT表示Tag表,我們可以講字段抽象化出來,也就是一個ID,一個Tag的String 同理對於標籤表也是如此。朋友們應該可以理解我的意思了。
 
對,就是做個代碼生成器把對應的多對多關係給生成出來,這個很好寫的,幾個Append就可以搞定。如果想更方便的處理,那麼把這個東西做成單件的模式抽象化出來,然後再違反一下原則,做成基類,其他關係繼承這個基類。。。。。剩下的應該很簡單了,具體實現大家思考吧。
 
讓併發來的更猛烈些吧,高併發環境下的數據處理方案
 
對於高併發性質的網站,在sns特別是webgame方面應該是最容易也是最難處理的地方了,容易處理的是如果是純粹基於數據庫驅動也就是select和update的問題,而難的地方也是不是select而是update,在高併發的驅動下,update經常會超時,雖然我們可以在finally把它處理掉,讓人鬱悶的是,數據庫連接池仍然會飽和,數據仍然會丟失….
 
上面的情況是非常常見的web項目失敗的原因之一,在數據飛速膨脹和併發呈幾何級增長的情況下,制約我們的可能是io,database本身的問題了,讓我們頭痛的是不管是哪種數據庫,Oracle也好,mysql也好,sqlserver也好都會timeout,而且是頻繁的timeout頻繁的Exception。這個時候就需要我們的應用程序在處理的前期就應該考慮到的,一個好的數據緩存策略常常決定了我們的成敗,而緩存策略也是web項目最難以測試和最容易出錯的地方。
 
在大型網站架構中,最關鍵最核心的也是緩存策略了,介於其複雜性,這裏只簡單的介紹一下基於高併發數據庫緩存方案,後面的將詳細介紹常用的緩存策略。這個方法與其叫緩存不如叫數據緩衝,其實也是異步更新數據,根據負載情況不同,我們哪怕僅僅將數據緩衝1秒,帶來的負載提升就已經非常好了。
 
實現原理很簡單,將併發的更新首先緩存到一個應用程序池中,然後定時查詢(注意這裏的方案應和緩存方案具體結合,這裏只介紹概要情況)。
 
傳統的update請求處理流程是:請求—》應用程序—》更新數據庫,如下圖:
 

 

 

數據緩衝和更新部分可以在數據層裏獨立實現,也就是update的傳遞的時候首先傳遞緩衝池,然後定時更新,這裏需要注意的數據緩衝池的還要做的另外一份工作就是全局的數據緩存,緩存數據更新到數據這段的時間間隔,我們可以理解爲臨時表,再提取上下文請求的即時信息的時候首先從緩衝池裏讀取(這裏有很多技巧,比如巧妙的利用cookie,session做;臨界條件判斷),流程如下圖所示

 

 

上面簡單的介紹了一下基於數據更新緩存的處理,下篇具體詳細介紹基於併發更新機制的詳細緩存處理機制
 

瘋狂代碼,大型網站架構系列之五,緩存策略設計概要

上篇對瘋狂代碼緩存配置進行了概要的設計,可能說的有點模糊了,有幾個朋友發了幾個問題探討了下,這裏有必要先澄清一個問題,和常見的緩存策略不同,我們的緩存策略將重點放在更新策略而不是隻讀策略上。只讀緩存以及共性緩存策略性質實現的難度並不大,我們要解決的是非共性緩存,併發更新緩存,可擴充性緩存,分佈式緩存更新運算的問題,而對於共性的東西的話我們可以很輕鬆的實現,而不必做太多的運算。
 
試想一個問題,對於一個多用戶的併發的系統,如果對每個用戶都維護一份緩存策略還要保證更新的及時性以及處理的必要性來說的話,我們很難想到一個有效的處理機制來維護每份(每用戶)緩存的副本的,緩存的存儲性質也決定了做分佈式緩存策略處理的難度和分佈式通訊更新的的難度,我們也很難嘗試對於一些訪問量很小且少有共性的頁面實現有效的緩存命中率,比如某某用戶的博客。
 
簡單的總結了一下關於緩存策略討論的重點
A.     基於海量非共性數據的緩存策略
B.      基於數據緩存級別併發更新的緩存策略
C.     基於數據併發存儲的緩存策略
D.     基於分佈式的緩存策略
E.基於搜索的緩存策略
 
我們這裏不再贅談關於頁面靜態化以及類似的問題,靜態化的情況非常適合在系統初期,用戶的基數並不算很大的情況下實現,而在涉及集羣的情況下,靜態化的實現成本,IO成本,維護成本,擴充成本以及更新成本會遠遠的超出緩存策略的成本,當然我們也會有一套建立在緩存基礎上的靜態化處理方案,這些放在以後再談。我們的目的是要建立一個可伸縮,便於維護擴展的緩存策略,下面就具體問題進行分析。
 
對於問題A:
常見的博客系統就是一個最好的例子,每個用戶的首頁都是相對個性的數據,共性的地方不多,以常見的處理方案來說的話,我們可能需要維護每個用戶訪問的緩存副本,而對於一些訪問量極小的博客站點來說的話這種方式無疑會造成巨大的浪費。
 
對於大量非共性的數據緩存來說,幾個處理方案:
1)      量化緩存目標並分配相應的緩存權值。(權值分級)
目的很簡單,只緩存有效的數據。首先抽取活躍用戶,以及高訪問量用戶,將數據進行分組分權制緩存(對於交友型的SNS系統來說,我們稱之爲美女效應)
2)      非連接持久性的緩存保持(臨時的持久性)
珍惜並有效利用數據查詢,將未被緩存命中時的查詢或者無權值的數據持久化保存(序列化存貯靜態存貯等),當緩存未被命中時優先取得持久化數據而非數據查詢。可以理解爲臨時數據存貯,或者臨時存貯於子服務器的某個位置。
3)  基於數據更新的緩存清除(一次性使用)
當持久性緩存保持失效(依賴數據發生修改),直接刪除臨時數據(緩存只在訪問時被激活並儲存,一旦修改或者失效,我們立刻拋棄)。
4)緩存更新代理規則
由另外的線程進行維護,並維護線程的有效性,最大限度的分離主程序對無效緩存以及臨時持久性緩存數據的清理
 
 
對於問題B:
在小型緩存策略中,緩存處理對於整個應用程序對於每個請求來說都是唯一的,可操作的和非物理存儲的。而在併發更新的過程中,一個小小的併發更新就會很現實的清空所有的緩存池,造成緩存命中率奇低而初始化率奇高而起不到緩存策略應有的作用。
 
在這種情況下,處理方案也和A.4中提到的方案是一樣的,由獨立的緩存更新進程來處理,對於應用程序中所有涉及緩存更新的請求由專門的更新代理來執行。這個處理方案相對簡單,不再贅述。
 
對於問題C:
上篇已經提到關於併發數據更新會帶來的問題也就是數據庫的I/O響應,超時,死鎖,以及線程的阻塞問題。我們用一個寫入緩存來處理這個方案,其實這個並非傳統意義上的讀緩存,姑且命名爲寫緩存吧,我們可以形象的理解爲類似硬盤緩衝區的問題。這裏處理的操作稍微有點多了,還要涉及只讀緩存的更新的問題了。
 
根據系統的不同,我們需要分析處理的角度也不同,我們以常見的webgame爲例來簡單介紹一下處理機制,這裏有兩種常見的情況
1) 對於webgame的最終用戶玩家來說,每個在線用戶的數據是非共性的(問題A),而在一個戰鬥場景下,每組數據時刻都在變化之中,如果我們對數據的變化採用數據庫日誌記錄的形式保存的情況顯然對Database的壓力很大,而我們需要記錄的僅僅是戰鬥的結果,戰鬥的過程我們完全沒有必要進行保存,這個時候我們就用寫入緩存來執行相應的數據操作。這個處理很簡單,用服務器變量的形式就能解決他。
2) 對於webgame的服務器角色來說,如果戰鬥場景的用戶量非常多,而數據更新非常大的情況下,我們採用方法1中的處理也可能力不從心,這個時候我們可以將緩存來進一步的抽象,在某個時間段內(比如3分鐘),維護一個唯一的緩存對象,所有的數據操作都在這個時間段來被緩存進程來記錄,來更新。而由另外的一個進程來進行異步的定時的數據保存操作。
 
對於問題D
       這個是比較常見的分佈式緩存服務器組了,而對緩存服務器來說其實要解決的問題就是服務器間之間互相通訊的問題,並保證數據一致性的問題。那麼我們的有四個處理規則:
1) 數據緩存應該被有效的分組並索引
目標是實現數據耦合的成都降到最低,甚至沒有耦合。比如以用戶ID爲分割的數據緩存分佈,或者以文章分類爲分割的緩存分佈
2) 數據緩存應該被有效的更新
如果數據被有效的分組完成後,這個就是問題C.2的方案了,和C.2不同的是,因爲緩存組可能未必在一組服務器中,可能涉及緩存和DATABASE數據通訊延遲的問題。這個時候要保證緩存服務器被即時的傳遞到databse,那麼需要另外的一個緩存檢測進程來完成這項工作(數據完整性檢查,並備份兩個緩存段的數據)
3) 緩存服務器間的數據完整性
對於無法分組的數據,比如時間段內的用戶認證數據和資料數據,我們需要保證兩組數據同步,最好的處理方法就是清除相應的緩存段,讓它在下次使用的時候初始化
4) 緩存服務器間的連通性
這個取決於物理線路,如果緩存服務器在天南地北的話,我們還需要一個隊列進程來進行同步和數據矯正,我們稱之爲緩存路由。
 
對於問題E
在分佈式緩存的情況下,多條件搜索往往涉及多個緩存服務器,處理起來筆者尚未有一套完善的出來方案。筆者用的是敷衍原則和集成原則了
敷衍原則:
對於搜索型的數據來說,很多情況下並不是非常重要,我們的搜索結果完全可以晚一會提供給用戶,允許搜索的數據有10分鐘或者更長時間的延遲。
集成原則
將搜索字段和表整合出來,用獨立的只讀查詢服務器來分擔負荷
 
 
如果您有比較好的方案,不妨mail:heroqst # gmail.com ,和瘋狂代碼探討下,請替換#爲@。
  
本文到這裏簡單的介紹了幾種緩存處理的方案,僅供參考。下篇將結合本文的緩存策略探討web 2.0下的數據規劃原則
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章