天天看點

高性能伺服器架構思路:緩沖政策 (一)

在伺服器端程式開發領域,性能問題一直是備受關注的重點。業界有大量的架構、元件、類庫都是以性能為賣點而廣為人知。然而,伺服器端程式在性能問題上應該有何種基本思路,這個卻很少被這些項目的文檔提及。本文正式希望介紹伺服器端解決性能問題的基本政策和經典實踐,并分為幾個部分來說明:

緩存政策的概念和執行個體

緩存政策的難點:不同特點的緩存資料的清理機制

分布政策的概念和執行個體

分布政策的難點:共享資料安全性與代碼複雜度的平衡

我們提到伺服器端性能問題的時候,往往會混淆不清。因為當我們通路一個伺服器時,出現服務卡住不能得到資料,就會認為是“性能問題”。但是實際上這個性能問題可能是有不同的原因,表現出來都是針對客戶請求的延遲很長甚至中斷。我們來看看這些原因有哪些:第一個是所謂并發數不足,也就是同時請求的客戶過多,導緻超過容納能力的客戶被拒絕服務,這種情況往往會因為伺服器記憶體耗盡而導緻的;第二個是處理延遲過長,也就是有一些客戶的請求處理時間已經超過使用者可以忍受的長度,這種情況常常表現為CPU占用滿額100%。

我們在伺服器開發的時候,最常用到的有下面這幾種硬體:CPU、記憶體、磁盤、網卡。其中CPU是代表計算機處理時間的,硬碟的空間一般很大,主要是讀寫磁盤會帶來比較大的處理延遲,而記憶體、網卡則是受存儲、帶寬的容量限制的。是以當我們的伺服器出現性能問題的時候,就是這幾個硬體某一個甚至幾個都出現負荷占滿的情況。這四個硬體的資源一般可以抽象成兩類:一類是時間資源,比如CPU和磁盤讀寫;一類是空間資源,比如記憶體和網卡帶寬。是以當我們的伺服器出現性能問題,有一個最基本的思路,就是——時間空間轉換。我們可以舉幾個例子來說明這個問題。

高性能伺服器架構思路:緩沖政策 (一)

水壩就是用水庫空間來換流量時間的例子

當我們通路一個WEB的網站的時候,輸入的URL位址會被伺服器變成對磁盤上某個檔案的讀取。如果有大量的使用者通路這個網站,每次的請求都會造成對磁盤的讀操作,可能會讓磁盤不堪重負,導緻無法即時讀取到檔案内容。但是如果我們寫的程式,會把讀取過一次的檔案内容,長時間的儲存在記憶體中,當有另外一個對同樣檔案的讀取時,就直接從記憶體中把資料傳回給用戶端,就無需去讓磁盤讀取了。由于使用者通路的檔案往往很集中,是以大量的請求可能都能從記憶體中找到儲存的副本,這樣就能大大提高伺服器能承載的通路量了。這種做法,就是用記憶體的空間,換取了磁盤的讀寫時間,屬于用空間換時間的政策。

高性能伺服器架構思路:緩沖政策 (一)

友善面預先緩存了大量的烹饪操作

舉另外一個例子:我們寫一個網絡遊戲的伺服器端程式,通過讀寫資料庫來提供玩家資料存檔。如果有大量玩家進入這個伺服器,必定有很多玩家的資料資料變化,比如更新、獲得武器等等,這些通過讀寫資料庫來實作的操作,可能會讓資料庫程序負荷過重,導緻玩家無法即時完成遊戲操作。我們會發現遊戲中的讀操作,大部分都是針是對一些靜态資料的,比如遊戲中的關卡資料、武器道具的具體資訊;而很多寫操作,實際上是會覆寫的,比如我的經驗值,可能每打一個怪都會增加幾十點,但是最後記錄的隻是最終的一個經驗值,而不會記錄下打怪的每個過程。是以我們也可以使用時空轉換的政策來提供性能:我們可以用記憶體,把那些遊戲中的靜态資料,都一次性讀取并儲存起來,這樣每次讀這些資料,都和資料庫無關了;而玩家的資料資料,則不是每次變化都去寫資料庫,而是先在記憶體中保持一個玩家資料的副本,所有的寫操作都先去寫記憶體中的結構,然後定期再由伺服器主動寫回到資料庫中,這樣可以把多次的寫資料庫操作變成一次寫操作,也能節省很多寫資料庫的消耗。這種做法也是用空間換時間的政策。

高性能伺服器架構思路:緩沖政策 (一)

拼裝家具很省運輸空間,但是安裝很費時

最後說說用時間換空間的例子:假設我們要開發一個企業通訊錄的資料存儲系統,客戶要求我們能儲存下通訊錄的每次新增、修改、删除操作,也就是這個資料的所有變更曆史,以便可以讓資料回退到任何一個過去的時間點。那麼我們最簡單的做法,就是這個資料在任何變化的時候,都拷貝一份副本。但是這樣會非常的浪費磁盤空間,因為這個資料本身變化的部分可能隻有很小一部分,但是要拷貝的副本可能很大。這種情況下,我們就可以在每次資料變化的時候,都記下一條記錄,内容就是資料變化的情況:插入了一條内容是某某的聯系方法、删除了一條某某的聯系方法……,這樣我們記錄的資料,僅僅就是變化的部分,而不需要拷貝很多份副本。當我們需要恢複到任何一個時間點的時候,隻需要按這些記錄依次對資料修改一遍,直到指定的時間點的記錄即可。這個恢複的時間可能會有點長,但是卻可以大大節省存儲空間。這就是用CPU的時間來換磁盤的存儲空間的政策。我們現在常見的MySQL InnoDB日志型資料表,以及SVN源代碼存儲,都是使用這種政策的。

另外,我們的Web伺服器,在發送HTML檔案内容的時候,往往也會先用ZIP壓縮,然後發送給浏覽器,浏覽器收到後要先解壓,然後才能顯示,這個也是用伺服器和用戶端的CPU時間,來換取網絡帶寬的空間。

高性能伺服器架構思路:緩沖政策 (一)

在我們的計算機體系中,緩存的思路幾乎無處不在,比如我們的CPU裡面就有1級緩存、2級緩存,他們就是為了用這些快速的存儲空間,換取對記憶體這種相對比較慢的存儲空間的等待時間。我們的顯示卡裡面也帶有大容量的緩存,他們是用來存儲顯示圖形的運算結果的。

高性能伺服器架構思路:緩沖政策 (一)

通往大空間的郊區路上容易交通堵塞

緩存的本質,除了讓“已經處理過的資料,不需要重複處理”以外,還有“以快速的資料存儲讀寫,代替較慢速的存儲讀寫”的政策。我們在選擇緩存政策進行時空轉換的時候,必須明确我們要轉換的時間和空間是否合理,是否能達到效果。比如早期有一些人會把WEB檔案緩存在分布式磁盤上(例如NFS),但是由于通過網絡通路磁盤本身就是一個比較慢的操作,而且還會占用可能就不充裕的網絡帶寬空間,導緻性能可能變得更慢。

在設計緩存機制的時候,我們還容易碰到另外一個風險,就是對緩存資料的程式設計處理問題。如果我們要緩存的資料,并不是完全無需處理直接讀寫的,而是需要讀入記憶體後,以某種語言的結構體或者對象來處理的,這就需要涉及到“序列化”和“反序列化”的問題。如果我們采用直接拷貝記憶體的方式來緩存資料,當我們的這些資料需要跨程序、甚至跨語言通路的時候,會出現那些指針、ID、句柄資料的失效。因為在另外一個程序空間裡,這些“标記型”的資料都是不存在的。是以我們需要更深入的對資料緩存的方法,我們可能會使用所謂深拷貝的方案,也就是跟着那些指針去找出目标記憶體的資料,一并拷貝。一些更現代的做法,則是使用所謂序列化方案來解決這個問題,也就是用一些明确定義了的“拷貝方法”來定義一個結構體,然後使用者就能明确的知道這個資料會被拷貝,直接取消了指針之類的記憶體位址資料的存在。比如著名的Protocol Buffer就能很友善的進行記憶體、磁盤、網絡位置的緩存;現在我們常見的JSON,也被一些系統用來作為緩存的資料格式。

但是我們需要注意的是,緩存的資料和我們程式真正要操作的資料,往往是需要進行一些拷貝和運算的,這就是序列化和反序列化的過程,這個過程很快,也有可能很慢。是以我們在選擇資料緩存結構的時候,必須要注意其轉換時間,否則你緩存的效果可能被這些資料拷貝、轉換消耗去很多,嚴重的甚至比不緩存更差。一般來說,緩存的資料越解決使用時的記憶體結構,其轉換速度就越快,在這點上,Protocol Buffer采用TLV編碼,就比不上直接memcpy的一個C結構體,但是比編碼成純文字的XML或者JSON要來的更快。因為編解碼的過程往往要進行複雜的查表映射,清單結構等操作。

相關推薦

下一篇 高性能伺服器架構思路:緩沖清理政策(二)

彈性緩存Memcached

緩存系統在遊戲業務中的特異性