[轉]伺服器效能和延展性殺手

來源:互聯網
上載者:User
伺服器|伸縮|效能 伺服器效能和延展性殺手
George V. Reilly
微軟公司
2月22, 1999

目錄

序言
應用程式伺服器
IIS的靈活性和效能
扼殺伺服器效能的十條戒律
結論

--------------------------------------------------------------------------------

序言

現在,伺服器效能問題是許多書寫傳統型應用程式的人所要面對的問題。元件物件模型(Component Object Model,COM)和Component ware的成功產生了一個意想不到的結果,這就是如果使用像ASP(IIS的一個擴充)這樣的應用程式伺服器,就不用編寫主機代碼了,其實以前的主機代碼都不是在真正的伺服器環境下編寫的。案頭環境和伺服器環境之間有許多重要的不同,這些不同會在效能上產生不可預測的影響。

傳統型應用程式伺服器

影響傳統型應用程式效能的因素是眾所周知的。長指令路徑意味著更慢的代碼,這是效能方面的一個主要缺陷。使用大量資源會使應用程式變得更加臃腫,這樣系統中的其他應用程式可用的資源就會更少。減慢啟動時間會激怒使用者。太多的回合設定會使機器的頁錯誤率增高,使它們變慢而且反映遲鈍。伺服器應用程式也常受到這些因素影響,另外還有一些其他因素介紹如下:

通常,伺服器應用程式同時處理的客戶沒有幾百也有幾十。對傳統型應用程式來說,如果能在1/10秒內對使用者做出反應就算是很快的了。假設一個操作需要整整100ms的話,那麼這個應用程式在一秒中只能進行10個操作。大多數伺服器應用程式需要比每秒鐘十次請求大得多的通量。高延遲時間網路(延遲時間=訊息的傳輸時間)加長了反應時間,這就需要伺服器的反應更快以滿足要求。

伺服器應用程式經常處理大量的資料設定。效率低下的,尤其是那些浪費已耗用時間的方法,是不能用於處理上百萬條資料的。

伺服器機器比案頭機器更強大。伺服器機器有更多的記憶體,更大的磁碟,更快的CPUs,並且通常有多個處理器。但是這些仍然不夠。案頭機器處理的是零星的突發性業務,大部分時間是閒置,而伺服器的負載是連續不斷的。伺服器機器很昂貴,必須運行得很好才行。

伺服器應用程式需要具有以月計算的正常已耗用時間。過了一段時間後,伺服器的效能必須不會由於資源流失或 cruft(一種需要周期性清除的資料結構和統計結果)的積聚而降低。

大多數伺服器應用程式都需要採用多線程結構。考慮一個一次只處理一個請求。而將大部分時間都化在I/O上的單線程伺服器,這樣的效能是很難讓人接受的。線程池可以利用其他閒置處理器刻度同時處理幾個請求。為了充分利用多處理器系統,伺服器應用程式必須是多線程的。不幸的是,多線程應用程式很難編寫,很難調試,而且很難運行得好,尤其是在多處理器系統中。但是一旦正確地得到它,其效能會遠遠超過同樣的單線程應用程式,從這一點來說,使用多線程應用程式還是值得的。

單線程應用程式相對簡單,很容易理解:程式中某一時刻只有一個事件發生。在多
線程應用程式中,並發行為導致複雜的相互作用,其影響很難預測。另外,這些相
互作用,不管是否是災難性的,都很難再生。傳統型應用程式很少有多於一個線程
的,即使有,這些線程也只是用於分立的後台業務,例如列印。


IIS的靈活性和效能

Internet Information Server(IIS)是一個應用程式伺服器。在很多方面,它像是一個虛擬作業系統,因為有許多ASP和ISAPI應用程式在處理間隔中運行。

IIS使用一個I/O線程池來處理所有到來的請求。對靜態檔案(.htm,.jpg等檔案)的請求會馬上得到滿足,而對動態內容的請求被指派到適當的ISAPI擴充動態串連庫。ASP擴充利用一個工人線程池運行ASP頁。因為ASP是基於COM的,所以所有組件都是在我們的處理過程中執行的。這是一個好壞摻半的事情。它對開發人員來說是好極了,因為它允許組件的簡單重用,使ASP非常靈活,因此使ASP和IIS非常成功。但是,這個靈活性導致了效能問題。因為許多組件是為案頭系統編寫的,並且許多專門為ASP建立的組件是由那些不是十分會寫高效能伺服器組件的人編寫的。

對ISAPI擴充和過濾器也是一樣。不同組件之間及同一組件的不同執行個體中都存在著嚴重的相互影響。

下面的所有說明都適用於IIS,其中的大多數也適用於其他伺服器應用程式。


扼殺伺服器效能的10條戒律

下面的每一條戒律都將有效地影響代碼的效能和延展性。換句話說,儘可能不要照著戒律去做!下面,我將解釋如何破壞他們以便提高效能和延展性。


應該分配和釋放多個對象

你應該盡量避免過量分配記憶體,因為記憶體配置可能是代價高昂的。釋放記憶體塊可能更昂貴,因為大多數分配算符總是企圖串連臨近的已釋放的記憶體塊成為更大的塊。直到Windows NT? 4.0 service pack 4.0,在多執行緒中,系統堆通常都運行得很糟。堆被一個全域鎖保護,並且在多處理器系統上是不可擴充的。

不應該考慮使用處理器快取

大多數人都知道由虛擬記憶體子系統導致的hard 頁錯誤代價很高,最好避免。但是許多人認為其他記憶體存取方法沒有什麼區別。自從80486以後,這一觀點就不對了。現代的CPUs比RAM要快得多,RAM至少需要兩級記憶體緩衝 ,高速L1 緩衝能儲存8KB資料和8KB指令,而較慢的L2 緩衝能儲存幾百KB的資料和代碼,這些資料和代碼混合在一起。L1 緩衝中記憶體地區的一個引用需要一個刻度,L2 緩衝的引用需要4到7個刻度,而主記憶體的引用需要許多個處理器刻度。後一數字不久將會超過100個刻度。在許多方面,緩衝像一個小型的,高速的,虛擬記憶體系統。

至於和緩衝有關的基本記憶體單元不是位元組而是緩衝列。Pentium 緩衝列有32個位元組寬。Alpha 緩衝列有64個位元組寬。這意味著在L1 緩衝中只有512個slot給代碼和資料。如果多個資料一起使用(時間位置)而並不儲存在一起(空間位置),效能會很差。數組的空間位置很好,而相互串連的列表和其他基於指標的資料結構的位置往往很差。

把資料打包到同一個緩衝列中通常會有利於提高效能,但是它也會破壞多處理器系統的效能。記憶體子系統很難協調處理器間的緩衝。如果一個被所有處理器使用的唯讀資料,和一個由一個處理器使用並頻繁更新的資料共用一個緩衝 列,那麼緩衝將會花費很長時間更新這個緩衝列的拷貝。這個Ping-Pong高速遊戲通常被稱為"緩衝 sloshing"。如果唯讀資料在一個不同的緩衝 列中,就可以避免sloshing。

對代碼進行空間最佳化比進行速度最佳化效率更高。代碼越少,代碼所佔的頁也越少,這樣需要的回合設定和產生的頁錯誤也會更少,同時佔據的緩衝 列也會更少。然而,某些核心函數應該進行速度最佳化。可以利用profiler去識別這些函數。

決不要緩衝頻繁使用的資料。

軟體緩衝可以被各種應用程式使用。當一個計算代價很高時,你會儲存結果的一個拷貝。這是一個典型的時空折中方法:犧牲一些儲存空間以節省時間。如果做得好,這種方法可能非常有效。

你必須正確地進行緩衝。如果緩衝了錯誤資料,就會浪費儲存空間。如果緩衝得太多,其他動作可以使用的記憶體將會很少。如果緩衝得太少,效率又會很低,因為你必須重新計算被緩衝 遺漏的資料。如果將時間敏感性資料緩衝得時間過長,這些資料將會過時。一般,伺服器更關心的是速度而不是空間,所以他們要比案頭系統進行更多的緩衝。一定要定期去除不用的緩衝,否則將會有回合設定問題。

應該建立多個線程,越多越好。

調整伺服器中起作用的線程數目是很重要的。如果線程是I/O-bound的,將會花費很多時間用來等待I/O的完成-一個被阻塞的線程就是一個不做任何有用工作的線程。加入額外的線程可以增加通量,但是加入過多的線程將會降低伺服器的效能,因為上下文交換將會成為一個重大的overhead。上下文交換速度應該低的原因有三個:上下文交換是單純的overhead,對應用程式的工作沒有任何益處;上下文交換用盡了寶貴的刻度;最糟的是,上下文交換將處理器的緩衝填滿了沒用的資料,替換這些資料是代價高昂的。

有很多事情是依靠你的線程化結構的。每個用戶端一個線程是絕對不合適的。因為對於大量使用者端,它的擴充性不好。上下文交換變得難以忍受,Windows NT用盡了資源。線程池模型會工作得更好,在這種方法中一個工人線程池將處理一條請求列,因為Windows 2000提供了相應的APIs,如QueueUserWorkItem。

應該對資料結構使用全域鎖

使資料安全執行緒的最簡單方法是把它套上一把大鎖。為簡單起見,所有的東西都用同一把鎖。這種方法會有一個問題:序列化。為了得到鎖,每一個要處理資料的線程都必須排隊等候。如果線程被一把鎖阻塞,它沒有在做任何有用的事。當伺服器的負載較輕時,這個問題並不常見,因為一次可能只有一個線程需要鎖。在負載很重的情況下,對鎖的激烈爭奪可能就會成為一個大問題。

設想在多車道高速公路上發生了一個意外事故,這條高速公路上的所有車輛都被轉向一條狹窄的道路。如果車輛很少,這一轉換對交通流的速率的影響可以忽略。如果車輛很多,當車輛慢慢併入那條單通道時,交通阻塞會延伸幾英裡。

有幾種技術能夠減少鎖競爭。

· 不要過分保護,也就是說,不是非常必要不要鎖住資料。只有需要時才去持有鎖,而且時間不要過長。不要在大段代碼周圍或頻繁執行的代碼中沒必要地使用鎖,這一點很重要。
· 對資料進行分割,使它能夠用一套獨立的鎖保護。例如,一個符號表可以按標識符的第一個字母分割,這樣在修改名字以Q開頭的符號的值時,就不會去讀名字以H開頭的符號的值。
· 使用APIs的Interlocked 系列(InterlockedIncrement,InterlockedCompareExchangePointer等)自動修改資料而不需要鎖。
· 當資料不是經常被修改時可以使用多讀者/單作者(multi-reader/single-writer)鎖。你將獲得更好的並發性,儘管鎖操作的代價將更高並且你可能會冒餓死作者的危險。
· 在關鍵區段使用迴圈計數器。參見Windows NT 4.0 service pack 3中的SetCriticalSectionSpinCount API。
· 如果你不能得到鎖,使用TryEnte



相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。