構建高性能web站點

來自專欄皓月繁星

帶寬的概念跟高速路不是一樣。而是類似於高速路的關卡,是跟數據的發送速度有關和限定出口帶寬的大小有關。

從a到b傳輸數據,帶寬的大小跟最小的哪一方決定。假如a的帶寬是10Mbps,b的帶寬是1Mbps,那麼就以1M來計算。(從TCP通信概念來理解就是滑動窗口控制通信每次發送的bit數)

假如發送10MB的數據來計算,位元組轉二進位位

10MB=80Mb

除以

1Mbps等於80秒,也就是傳完所有數據約等於80秒。

因為響應時間=發送時間(本地IO處理時間)+傳輸時間。而傳輸時間類似光速,可以忽略不計。

下載速度=文件總大小/響應時間=10MB/80s=12KB/s

而電信號傳播的速度跟光速接近。北京到深圳的所花時間幾乎為0可以忽略。

數據包是重疊發送,同時有多個數據包在路上,不是發完一個包確認收到再發一個包。

IO模型:

同步阻塞IO,類似你去美食街買麵條喫,要一直等麵條煮好,喫了才能離開,很耗費時間。(一直佔用CPU)

同步非阻塞IO,你讓師傅煮麵條,自己不用在店裡等,可以出去逛,但要定期回來看是否煮好了,雖然能喫上麵條,但跑來跑去也很累。(不斷輪詢使用CPU)

多路IO就緒通知。就是你讓師傅煮麵條,自己出去逛,美食街有一個大的電子屏,裡面顯示麵條是否煮好了。你只要關注這個電子屏即可。(文件描述符)。其中 select,poll,epoll,都是IO模型文件描述符的實現。至於出現這麼多不同的實現方式是由於早期UNIX版本分化造成。

內存映射:

將磁碟文件映射到內存,操作這塊內存就同步到磁碟文件。無須用read,write等系統調用來訪問文件,而是通過mmap系統調用來建立內存和磁碟文件的關聯,然後像內存一樣自由地訪問文件。

sendfile:

對於傳統的read系統調用。web伺服器往外發數據,磁碟文件的數據先要經過內核緩衝區,然後到達用戶內存空間,再送到網卡對應的內核緩衝區,接著再被送入網卡進行發送。繞了一圈,很浪費時間,於是開發了sendfile系統調用。可以將磁碟文件的特定部分直接傳送到代表客戶端的socket描述符,加快了靜態文件的請求速度。

但是對於處理小文件請求時,sendfile表現就不是發揮的很好,因為發大量小文件時,更多的是處理TCP連接而發數據的環節在整個過程中所佔時間的比例相比於大文件請求要小很多。

非同步IO:

阻塞和非阻塞是指當進程訪問的數據如果尚未就緒,進程是否需要等待,簡單說這相當於函數內部的實現區別,即未就緒時是直接返回還是等待就緒。

同步和非同步是指訪問數據的機制,同步一般指主動請求並等待IO操作完畢的方式,當數據就緒後再讀寫的時候必須阻塞,非同步則指主動請求數據後便可以繼續處理其他任務,隨後等待IO操作完畢的通知,這可以使進程在數據讀寫時也不發生阻塞。

一個進程處理一個連接:

apache處理連接的方式是多進程,主進程負責accept來自客戶端的連接,然後fork一個新的worker進程處理請求。多進程的方式的優點在於每個進程獨立,多個進程之間互不影響,穩定性好。但缺點在於耗費內存,因此限制了並發連接數。

一個線程處理一個連接:

用多線程的方式處理連接。優點在於省內存,多線程開銷小。缺點在於要處理線程同步等複雜問題,因此容易積累問題,需要定期重啟伺服器。如IIS。

一個進程處理多個連接:

Nginx就是採用這種方式。主進程開闢多個工作進程,每個工作進程同時處理多個連接。

動態內容緩存:

緩衝是減緩衝擊力。目的在於改善各部件之間由於通信速度不同的而引發的問題。比如將用戶態地址空間的數據寫入磁碟時,在內存設置緩衝區,讓數據源源不斷地地流進緩衝區,再由緩衝區負責寫入磁碟。

而緩存的目的在於數據復用,加快數據的訪問速度。

頁面緩存:

將動態腳本計算出來的結果組合成HTML緩存起來。如smarty框架。

動態腳本加速:

將PHP的中間代碼opcode進行緩存。通過APC實現。修改php.ini配置。減少CPU和內存開銷。另一款opcode緩存器,XCache。

瀏覽器緩存:

動態程序通過與瀏覽器交流,告訴瀏覽器的緩存策略。緩存那些文件,緩存時間多久。

通過Expires和Cache-Control標記緩存策略。

WEB伺服器緩存:

通過URL作為key,對應的HTML作為value值。實現緩存。

可以通過Nginx,varnish等工具做web伺服器緩存。並且要監視緩存命中率。

WEB組件分離:

由於不同的組件文件特性不一樣,比如動態腳本,圖片,js腳本,css。它們各自文件大小,內容更新頻率,並發量,是否需要CPU計算,有很大的不同,因此把它們做分離。能夠根據不同的特性分配不同的硬體配置。分配獨立的域名。

瀏覽器對於一個域名限制了最大並發數,如果我把組件分開多個不同域名,那麼訪問我網站的時候就能夠同時有多個並發數,加快訪問速度。

動態腳本的機器需要更多的CPU計算和內存。

靜態內容需要更多的IO操作,所以是IO密集型的。非同步IO,更快的磁碟,更高帶寬,HTTP持久連接。大文件傳輸採用 sendfile 方式訪問文件。

資料庫性能優化:

mysqlreport能夠顯示MySQL的工作狀態,幫助我們分析性能。任何優化之前需要找到優化點,而不是盲目的猜測。

用 explain 來分析SQL查詢語句的索引使用情況,幫助我們正確的使用索引。

通過配置my.cnf來開啟慢查詢日誌,分析優化慢查詢。可以結合mysqlsla來使用。

在查詢過程中需要創建臨時表來存儲中間數據,我們需要通過合理的索引來避免它。但臨時表在所難免,我們也要盡量減少臨時表本身的開銷。MySQL可以將臨時表創建在磁碟,內存以及臨時文件中。在磁碟上創建臨時表的開銷最大,因此我們可以通過tmp_table_size選項來設置用於存儲臨時表的內存空間大小,一旦這個空間不夠用MySQL將會啟用磁碟來保存臨時表。

MySQL採用多線程來處理並發的連接,通過MySQLreport中的threads部分看到統計結果。

通過反範式設計來減少聯表查詢,提高查詢速度。

在某些情況下,採用NoSQL能夠解決MySQL弱項的問題。

WEB負載均衡:

HTTP重定向方式。

DNS輪詢。缺點在於靈活性弱,沒有人能直接看到DNS伺服器解析到了那一臺伺服器。無法很好的控制調度策略。

反向代理。可以控制按權重分配任務。可控的負載均衡策略。

IP負載均衡。可以通過NAT或者iptables、LVS方式實現。

共享文件:

NFS和Samba都是共享文件系統的實現。

NFS通過RPC協議來傳輸文件,底層也是通過UDP通信。只要將遠程伺服器的文件目錄映射到客戶端,客戶端機器就能像本地一樣訪問遠程文件。

侷限性,影響性能的因素包括NFS伺服器磁碟吞吐率和網路帶寬。

內容分發和同步:

NFS等共享文件系統雖然能夠幫助我們在多臺伺服器之間共享文件,但是在這種機制下,不論是性能還是可用性,都無法達到更高的要求。而且它做不到無限水平擴展,更像是一個中心化的設計模型。

因此需要通過文件複製來解決去中心問題。備份多個副本。通過SSH+SCP,WebDav,rsync或者SFTP方式分發文件。

分散式文件系統:

上面通過共享文件和文件分發的方式實現可水平擴展文件系統。但是這種方式太繁瑣,容易出錯。

可以直接採用開源的分散式文件系統。Hadoop。分散式文件系統並不是傳統意義上的文件系統,文件不是存儲在磁碟上而是工作在操作系統的用戶空間,由應用程序來實現。是一個抽象層的實現。由於它存儲的不是真正的磁碟文件,因此它不直接提供HTTP訪問文件。

分散式文件系統自身提供方便的文件管理手段,並且會保存文件的副本。

分散式計算:

Gearman是開源產品,用來實現遠程函數調用。跨語言,跨設備。因此可以將任務分發到多個機器上,相互調用。

map/reduce是一種分散式並行計算的開發框架。可以拆分任務,並行運行,再匯總結果。

性能監控:

Nmon,Cacti,Nagios是工作在伺服器本地的實時監控軟體。監控CPU,磁碟IO等工作狀態。

推薦閱讀:

相關文章