百萬級訪問網站前期的技術準備

 

   (若是感受有幫助,請幫忙點推薦,添加關注,謝謝!你的支持是我不斷更新文章的動力。本博客會逐步推出一系列的關於大型網站架構、分佈式應用、設計模式、架構模式等方面的系列文章)css

1、服務器硬件  前端

  建議至少三臺的標準配置,分別用做web處理數據庫備份java

  web服務器至少要8G內存,雙sata raid1若是經濟稍微寬鬆,或靜態文件或圖片多,則15k sas raid1+0數據庫至少16G內存,15k sas raid 1+0。備份服務器最好跟數據庫服務器同等配置。硬件能夠本身買品牌的底板,也就是機箱配主板和硬盤盒,CPU內存硬盤都本身配,也能夠上整套品牌,也可 以兼容機。三臺機器,市場行情67萬也就配齊了。mysql

  web服務器能夠既跑程序又當內存緩存,數據庫服務器則只跑主數據庫(假如是MySQL的話),備份服務器乾的活就相對多一些,web配置、緩存配置、數據庫配置都要跟前兩臺一致,這樣WEB和數據庫任意一臺出問題,把備份服務器換個ip就切換上去了。備份策略,能夠drbd,能夠rsync,或者其餘的不少不少的開源備份方案可選擇。rsync最簡單,放cron裏本身跑就行。備份和切換,建議多作測試,選最安全最適合業務的,而且儘量異地備份。nginx

 

2、架構  web

  初期架構通常比較簡單,web負載均衡+數據庫主從+緩存+分佈式存儲+隊列。sql

  只是您比其餘人厲害之處就在於設計上考慮到緩存失效時的雪崩效應、主從同步的數據一致性和時間差、隊列的穩定性和失敗後的重試策略、文件存儲的效率和備份方式等等意外狀況。數據庫

      緩存總有一天會失效,數據庫複製總有一天會斷掉,隊列總有一天會寫不進去,電源總有一天會燒壞。根據墨菲定律,若是不考慮這些,網站遲早會成爲茶几。編程

 

3、服務器軟件  json

  Linuxnginxjava、mysqlsvn幾乎是標配,

  以上準備完畢,如今咱們有了運行環境,有了基本架構骨架,有了備份和切換方案,應該開始着手設計開發方面的事情了。

 

4、數據庫

  幾乎全部操做最後都要落到數據庫身上,它又最難擴展(存儲也挺難)。對於mysql,什麼樣的表用myisam,什麼樣的表用innodb,在開發以前要肯定。複製策略、分片策略,也要肯定。表引擎方面,通常,更新很少、不須要事務的表能夠用myisam,須要行鎖定、事務支持的,用innodb。 myisam的鎖表不必定是性能低下的根源,innodb也不必定全是行鎖,具體細節要多看相關的文檔,熟悉了引擎特性才能用的更好。現代WEB應用越來 越複雜了,咱們設計表結構時經常設計不少冗餘,雖然不符合傳統範式,但爲了速度考慮仍是值得的,要求高的狀況下甚至要杜絕聯合查詢。編程時得多注意數據一致性。

  複製策略方面,多主多從結構也最好一開始就設計好,代碼直接按照多主多歷來編寫,用一些小技巧來避免複製延時問題,而且還要解決多數據庫數據是否一致,能夠本身寫或者找現成的運維工具。

  分片策略。總會有那麼幾個表數據量超大,這時分片必不可免。分片有不少策略,從簡單的分區到根據熱度自動調整,依照具體業務選擇一個適合本身的。避免自增ID做爲主鍵,不利於分片。

  用存儲過程是比較難擴展的,這種情形多發生於傳統C/S,特別是OA系統轉換過來的開發人員。低成本網站不是一兩臺小型機跑一個數據庫處理全部業務的模式,是機海做戰。方便水平擴展比那點預分析時間和網絡傳輸流量要重要的多的多。

  NoSQL只是一個概念。實際應用中,網站有着愈來愈多的密集寫操做、上億的簡單關係數據讀取、熱備等,這都不是傳統關係數據庫所擅長的,因而就產生了不少非關係型數據庫,好比Redis/TC&TT/MongoDB/Memcachedb等,在測試中,這些幾乎都達到了每秒至少一萬次的寫操做,內存型的甚至5萬以上。例如MongoDB,幾句配置就能夠組建一個複製+自動分片+failover的環境,文檔化的存儲也簡化了傳統設計庫結構再開發的模式。不少業務是能夠用這類數據庫來替代mysql的。

 

5、緩存 

  數據庫很脆弱,必定要有緩存在前面擋着,其實咱們優化速度,幾乎就是優化緩存,能用緩存的地方,就不要再跑到後端數據庫那折騰。緩存有持久化緩存、內存緩存,生成靜態頁面是最容易理解的持久化緩存了,還有不少好比varnish的分塊緩存、前面提到的memcachedb等內存緩存,memcached首當其衝。緩存更新可用被動更新和主動更新。被動更新的好處是設計簡單,緩存空了就自動去數據庫取數據再把緩存填上,但容易引起雪 崩效應,一旦緩存大面積失效,數據庫的壓力直線上升極可能掛掉。主動緩存可避免這點可是可能引起程序取不到數據的問題。這二者之間如何配合,程序設計要多動腦筋。

 

6、隊列  

  用戶一個操做極可能引起一系列資源和功能的調動,這些調動若是同時發生,壓力沒法控制,用戶體驗也很差,能夠把這樣一些操做放入隊列,由另幾個模塊去異步執行,例如發送郵件,發送手機短信。開源隊列服務器不少,性能要求不高用數據庫當作隊列也能夠,只要保證程序讀寫隊列的接口不變,底層隊列服務可隨時更換就能夠,相似Zend Framework裏的Zend_Queue類,java.util.Queue接口等。

  

7、文件存儲  

  除告終構化數據,咱們常常要存放其餘的數據,像圖片之類的。這類數據數量繁多、訪問量大。典型的就是圖片,從用戶頭像到用戶上傳的照片,還要生成不一樣的縮略圖尺寸。存儲的分佈幾乎跟數據庫擴展同樣艱難。不使用專業存儲的狀況下,基本都是靠本身的NAS。這就涉及到結構。拿圖片存儲舉例,圖片是很是容易產生熱點的,有些圖片上傳後就再也不有人看,有些可能天天被訪問數十萬次,並且大量小文件的異步備份也很耗費時間。

  爲了未來圖片走cdn作準備,一開始最好就將圖片的域名分開,且不用主域名。不少網站都將cookie設置到了.domain.ltd,若是圖片也在這個域名下,極可能由於cookie而形成緩存失效,而且佔多餘流量,還可能由於瀏覽器併發線程限制形成訪問緩慢。

  若是用普通的文件系統存儲圖片,有一個簡單的方法。計算文件的hash值,好比md5,以結果第一位做爲第一級目錄,這樣第一級有16個目錄。從0F,能夠把這個字母做爲域名,0.yourimg.comf.yourimg.com(客戶端dns壓力會增大),還能夠擴展到最多16NAS集羣 上。第二級可用年月例如,201011,第三級用日,第四級可選,根據上傳量,好比am/pm,甚至小時。最終的目錄結構可能會是 e/201008/25/am/e43ae391c839d82801920cf.jpgrsync備份時能夠用腳本只同步某年某日某時的文件,避免計算大量文件帶來的開銷。

  固然最好是能用專門的分佈式文件系統或更專業點的存儲解決方案。

 

 

下面,咱們要談談代碼了

  開始設計代碼結構以前,先回顧一下以前準備過的事情:咱們有負載均衡的WEB服務器,有主從DB服務器並可能分片,有緩存,有可擴展的存儲。在組織代碼的各個方面,跟這些準備息息相關,我一二三的列出來分別說,而且每一條都以前面講到這個經典句式開頭,爲了方便對照。

  彆着急看經典句式,我思惟跳躍了,插一段。

  實際開發中,咱們總會在性能和代碼優雅性上做折中。對於當今的計算機和語言解釋器,多幾層少幾層對象調用、聲明變量爲Map仍是HashMap這種問題是最後才須要考慮的問題,永遠要考慮系統最慢的部分,從最慢的部分解決。

  例如看看你用的ORM是否是作了不少你用不到的事情,是否是有重複的數據調用。咱們作的是web應用開發,不是底層框架API,代碼易讀易懂是保證質量很重要的一方面,你的程序是爲了什麼而設計,有不一樣的方法……算了,這個話題另起一篇文章來講,扯遠了,咱繼續……

  前面講到,WEB服務器是要作負載均衡的,圖片服務器是要分開的。對於這點,代碼在處理客戶端狀態時,不要把狀態放到單機上,舉例,不要用文件session,嗯,常識。 

  若是有可能,最好在一開始就作好用戶單點認證的統一接口,包括跨域如何判斷狀態、靜態頁面如何判斷狀態,須要登陸時的跳轉和返回參數定義,底層給好接口,應用層直接就用(可參考GAE的 user服務)。登陸方面的設計要考慮移動設備的特性,好比電腦能夠用浮動層窗口,但NOKIA自帶的瀏覽器或UCWEB就沒法處理這種表現形式,程序必定既能處理AJAX請求又能直接經過URL來處理請求。圖片服務器分開,資源文件最好也佈局到圖片服務器,也就是WEB服務器只服務動態程序。雖然開發測試時稍微複雜(由於須要絕對URI才能訪問),但未來頁面前端優化上會輕鬆許多,而且你的WEB服務器IO優化也輕鬆許多。程序引用資源文件時,要有一個 統一的處理方法,在方法內部能夠自動完成不少事情,例如將css/js根據組合,拼成一個文件,或者自動在生成的URI後面加上QUERYSTRING, 若是未來前端用了緩存服務,那生成QUERYSTRING是最簡單的刷新服務端緩存和客戶端緩存的辦法。

  前面講到,數據庫會有複製,可能會多主多從,可能會分片。咱們程序在處理數據的過程當中,最好能抽象出來單獨放作一層。拿如今流行的MVC模式來講,就是在M層下方再放一個數據層,這個數據層不是一般所說的JDBC/PDO/ActiveRecord等,而是你本身的存取數據層,僅對外暴露方法,隱藏數據存取細節。這個數據層內部不要怕寫的難看,但必定要提供全部的數據存儲功能,其餘任何層次不要看到跟數據庫打交道的字眼。之因此這樣作,是由於在單關係數據庫的狀況下,可能會SELECT…JOIN…或直接INSERT…INTO…,可你可能會將一些表放到key-value數據庫裏存儲,或者分片,這麼作以後原來的語句和方式要所有改變,若是過於分散,則移植時會耗費很大精力,或獲得一個很大的Model

  在數據層面的設計上,儘可能避免JOIN查詢,咱們能夠多作冗餘,多作緩存,每種數據儘可能只須要一次查詢,而後在你的程序裏面進行組合。對於比較複雜的數據組合,在實時性要求不高的狀況下,可採用異步處理,用戶訪問時只取處理後的結果。在對於主鍵的處理上,避免使用自增ID,能夠用必定規則生成的惟一值當作主鍵,這種主鍵是最簡單的分片分佈策略。即便用自增ID,也最好用一個自增ID發生器,不然從數據庫不當心被寫了一下,那主鍵很容易衝突。

 

  前面講到,咱數據庫前面還有某些緩存擋着。別把mysqlquery cache當緩存,應用稍複雜的時候QUERY CACHE反而會成爲累贅。緩存跟數據庫和業務結合的很緊密,正由於跟業務關係緊密,因此這點沒有放之四海而皆準的方法。但咱們仍是有一些規則可參照。

  規則一:越接近前端,緩存的顆粒度越大。例如在WEB最前端緩存整個頁面,再日後一層緩存部分頁面區域,再日後緩存區域內的單條記錄。由於越靠近後端,咱們的可操做性越靈活,而且變化最多的前端代碼也比較方便編寫。在實踐中,由於產品需求變化速度很是快,迭代週期愈來愈短,有時很難將Controller和 Model分的那麼清楚,Controller層面處理部分緩存必不可免,但要保證若是出現這種狀況,Controller所操做的緩存必定不要影響其餘 數據需求方,也就是要保證這個緩存數據只有這一個Controller在用。

  規則二:沒有緩存時程序不能出錯。在不考慮緩存失效引起的雪崩效應時,你的程序要有緩存跟沒緩存一個樣,不能像新浪微博同樣,緩存一失效,粉絲微博全空,整個應用都亂套了。在緩存必不可少的狀況下,給用戶出錯信息都比給一個讓人誤 解的信息強。

  規則三,緩存更新要保證原子性或稱做線程安全,特別是採用被動緩存的方式時,極可能兩個用戶訪問時致使同一個緩存被更新,一般狀況這不是大問 題,可緩存失效後重建時極可能是引起連鎖反應的緣由之一。

  規則四:緩存也是有成本的。不僅是技術成本,還有人工時間成本。若是一個功能使用緩存和不使用, 在可預見的訪問量狀況下區別微小,但使用緩存會使複雜度增長,那就不用,咱們能夠加個TODO標註,在下次迭代的時候加上緩存處理。

 

  前面講到,文件存儲是獨立的,那麼全部的文件操做就都是遠程調用。能夠在文件服務器上提供一個很簡單的RESTful接口,也能夠提供xmlrpc json serveiceWEB服務器端所生成和處理的文件,所有經過接口通知文件服務器去處理,WEB服務器自己不要提供任何文件存儲。你會發現不少大網站的上傳圖片跟保存文章是分兩步完成的,就是基於這個緣由。

  以上幾條前面講到,其實無數人都講過,我也只是結合前幾篇文章用本身的話重複了一遍,真正分析起來精髓很簡單——除了良好的功能邏輯分層,咱們還要爲數據庫存儲、緩存、隊列、文件服務等程序外層資源調用單獨設計接口,你能夠把你的程序想象成是運行在Amazon EC2上並用他的全部web service服務,你的數據庫就是它的SimpleDB,你的隊列就是他的SQS,你的存儲就是他的S3,惟一不一樣是amazon的接口是遠程調用,你的是內部調用。

  將支撐服務接口化,意味着將MySQL更換到PostgreSQL不須要更改業務處理程序,移植團隊甚至不須要跟業務開發團隊過多溝通;意味着業務開發團隊是對接口編程而不是對數據庫編程;意味着不會由於某個業務開發人員的失誤而拖垮性能。

 

  對程序掃盲不感興趣的直接看這裏——

  沒有100%也有99.9%的網站安裝了訪問統計代碼.

相關文章
相關標籤/搜索