淺談效能優化
參考《大型網站技術架構》,其中包含一些個人理解,如有錯誤或說的不清楚的地方,請一定指出,謝謝。
這篇文章講的比較淺,如果有更深入的需要,歡迎留言或是查閱資料。
在談效能優化之前,我們先來看一看什麼是效能。
網站效能是客觀的指標,可以具體體現到響應時間、吞吐量等技術指標;同時也是主觀的感受,而感受是一種與具體參與者相關的東西,使用者間的感受、使用者與工程師間的感受都是不同的。
使用者感受到的響應時間,從使用者發出請求(比如點選按鈕)開始,包括使用者計算機和網站伺服器通訊的時間(網路傳輸的時間,可能是運維同學關注的點)、網站伺服器處理的時間(作為一個普通後端CRUD工程師,這個是我們關注的重點)、使用者計算機瀏覽器構造請求解析響應資料的時間(前段同學優化所關注的點)。
我們做一個網站,面向的是使用者,那麼,要提高網站效能,也可以從提高使用者體驗開始。
1.讓使用者感到快
同類型的兩個網站,A網站伺服器平均每個請求的處理時間是500毫秒,B網站伺服器平均每個請求的處理時間是1秒。使用者普遍反映B網站的速度快,為什麼?
經過觀察,A網站在請求處理過程中,給使用者呈現的是一片空白的介面;而反觀B網站,雖然請求處理慢,但在使用者等待過程中,使用者看到的是一個小雞不斷地下蛋。
大多數使用者被小雞下蛋的動畫吸引了,就不覺得等待無聊,就會覺得快。
所以,我們想提高效能,最主要的目的是要提高使用者體驗,不一定非要加索引、讀寫分離、程式碼優化等等等。有時候,加一個小小的等待動畫即可。
2.前端優化
本人是小後端,這一部分屬於紙上談兵了,如有錯誤,歡迎指出、辱罵.......(可憐臉)
我們也不能僅僅加一個動畫,如果響應時間過長,就算讓使用者看一個動畫片,長此以往,使用者也會不耐煩的。所以讓我們來愉快地開始我們真正的優化吧!
2.1 前端程式碼優化
2.1.1 減少http請求
我們所用的http是基於tcp的,還記不記得?tcp傳輸首先要經過三次握手建立連線,因此,代價是挺高的。
我們可以儘量減少請求來優化效能,手段有:合併css,合併js,合併圖片。將瀏覽器一次訪問需要的js、css合併為一個檔案,這樣瀏覽器只需要一次請求。
2.1.2 使用瀏覽器快取
對於更新頻率較低的靜態資源,總是傳輸沒有什麼意義,可以將他們快取在瀏覽器中,來改善效能。通過設定http頭中的Cache-Control和Expires屬性,可以設定瀏覽器快取。
如果靜態資源發生變化需要及時應用到客戶端瀏覽器,我們可以通過改變檔名來實現。
使用瀏覽器快取策略的網站在更新靜態資源時,應採用批量更新的方法,比如需要更新10個圖示檔案,不宜把10個檔案一次全部更新,而是應一個檔案一個檔案的更新,並有一定是間隔時間,以免使用者瀏覽器突然大量快取失效,集中更新快取,產生大量的網路傳輸,造成伺服器負載驟增、網路堵塞的情況。
2.1.3 啟用壓縮
在伺服器端對檔案進行壓縮,在瀏覽器端對檔案解壓縮,可有效減少網路傳輸的資料量。
但此方法會佔用瀏覽器、伺服器的資源(例如CPU、記憶體等)。因此,在頻寬不是瓶頸,而伺服器資源不足等情況下慎用。
2.1.4 CSS在上方,JS在下方
瀏覽器會在下載完全部CSS之後才對整個頁面進行渲染,因此較好的做法是將CSS放在頁面最上面,讓瀏覽器儘快下載CSS。JS則相反,瀏覽器在載入JS後立即執行,有可能會阻塞整個頁面,造成頁面顯示緩慢。
但如果頁面解析時就要用到JS,則放在下面就不合適了。
2.1.5 減少cookie傳輸
cookie包含在每次請求和響應中,因此cookie中儘量不要放太多東西。
2.2 前端架構優化
個人覺得,在web伺服器前的東西都屬於前端範圍,包括CDN、反向代理、負載均衡等。
我們先從CDN說起。
2.2.1 CDN加速
CDN其實本質上依然是一個快取,只不過部署在網路運營商的機房。因此該快取就處於裡使用者最近的地方,使用者就可以以最快的速度獲取資料,即我們之前學網路的時候所學的網路訪問的第一跳。
CDN一般快取一些訪問頻度很高的靜態資源,如圖片、檔案、CSS等。
2.2.2 反向代理加速
我們的反向代理,其實也可以通過配置快取功能加速WEB請求,其實本質上還是一個快取,就不多說了。
2.3 應用伺服器的效能優化
鐺鐺鐺,終於到我們激動人心的後端時刻了。
作為一個後端同學,我關注的主要是介面的速率,那麼介面怎麼加速呢?無非是使用快取加速資料讀取、使用叢集提高吞吐能力、使用非同步訊息加快請求響應、對程式碼進行優化從而改善程式效能。
2.3.1 分散式快取
又是快取!看來快取真的很重要,其實也不用奇怪,畢竟磁碟訪問是毫秒級的,而記憶體訪問是微秒級的,想要加速,把磁碟中的熱點資料放在記憶體中快取,是一個很好的選擇。其次,如果我們快取計算後的結果,那麼下次我們需要這個結果的時候,我們就無需再進行一次計算,也省了計算的時間。
可見,快取對於我們,是很重要的一個東西,因此,也有有心人利用這一點,搞出了許多攻擊,扯遠了。。。
快取本質上是一個雜湊表,熟悉資料結構的同學們知道,讀寫雜湊表的時間複雜度為O(1),因此,它很快!
我們用快取主要來儲存那些讀寫比很高、很少變化的資料,比如商品的類目資訊、熱門商品、熱搜等等。
應用程式讀取資料時,先看看快取中有沒有,如果讀取不到或已過期,則到資料庫中讀取(寫到這裡,我的腦海中已經浮現出了一大堆程式碼哇咔咔)。
2.3.2 使用叢集
再網站高併發訪問的場景下,使用負載均衡技術為一個應用構建一個由多臺伺服器組成的伺服器叢集,並將請求分發到多臺伺服器上處理,從而降低單個伺服器的負載,加快響應速度。
2.3.3 非同步
在我們沒有引入訊息佇列的時期,使用者的請求直接進入資料庫,而我們知道,資料庫讀寫是磁碟IO,很慢,那麼人們為了提高吞吐量,引入了訊息佇列,使用者的請求寫入訊息佇列後立即返回,再由訊息佇列的消費者(此情此景,消費者是資料庫)讀取訊息佇列中的訊息,非同步處理(此情此景,是寫入資料庫),由於訊息佇列速度遠快於資料庫(這時同學可能會疑惑,拿Kafka來說,同樣是操作磁碟,為何Kafka快呢?這時因為Kafka被設計成避免隨機IO,而資料庫操作充滿了隨機IO),因此使用者的響應延遲可以得到有效的改善。
2.3.4 程式碼優化
現在開始貼近我們可愛的普通後端開發同學的生活了,作為一個可愛的普通後端開發,我每天的工作就是增刪改查,那麼我就不用學習了嗎?不是的。
比如說,我們要在for迴圈中通過網路請求呼叫別人的介面,那麼我們想,這個for迴圈主要是IO操作,我們可以用多執行緒來提高它的效率,最大限度地利用我們的資源。