架構設計中影響效能的因素及解決方案
效能(performance)設計非常重要,對於伺服器端實時交易系統來說系統性能的重要性不言而喻,對客戶端軟體來說效能好的軟體也會獲得良好的使用者體驗,從而給使用者留下高質量軟體的良好印象。因此在進行架構設計中效能設計非常重要。
但架構設計實際是一個平衡設計,在可用性、可擴充套件性、可維護性、可靠性、高效能等之間做個妥協選擇。這些非功能性的需求再加上覆雜的功能性需求,同時還要考慮到專案管理上tight schedule, low cost, perfect effect的三角難題約束,有時需求還不是很明確,vision不是很清楚,這種情況下系統架構設計真是一門藝術。(可參考筆者另外一篇文章《》)
單就效能設計來說,在架構設計初期就一定要把系統性能考慮在內,否則等開發完成以後測試發現效能不好就比較難辦,通常要花費較長的時間來診斷效能瓶頸,找到提升的辦法,甚至要改變架構,傷筋動骨,往往造成專案延期。所以效能設計首先要有明確的效能目標,根據使用者和軟體本身的效能要求來設計,合適的就是最好的。其次,要有適當的度量標準和量化的效能指標。最後,要有相應的設計策略,具體的測試方法。
根據我的經驗,影響系統性能主要瓶頸在I/O,包括資料庫,socket,網路通訊,檔案等,例如頻繁查詢資料庫並返回大量結果集,頻繁操作大檔案等,這些昂貴的操作會佔用大量的CPU時間。拿系統響應和服務一個事務來說,有幾個Round trip,要通過哪幾層I/O,如何合理的分配這些I/O的呼叫,降低不必要的I/O,都是進行系統性能設計要考慮的。而有些效能問題在初期並不會表現出來,但當拿到實際上線環境下,存在多使用者併發、大資料量的情況下就會暴露出嚴重的問題。所以效能設計時一定要考慮到I/O,同步,併發,資源爭用,以及大資料量等因素。通常,I/O操作、網路響應、差的演算法、資料庫、以及其他的低效的資源使用都會導致低劣的效能。
具體可用的設計策略有:
l 快取以及快取層(caching layer)
在資料層和應用層之間增加資料快取層,提供全域性資料服務。可以大大減少資料庫往返次數。與讀取資料庫和讀取大檔案(如XML檔案)比,讀取記憶體的速度無疑要快的多。所以對經常要訪問的資料進行快取是非常好的實踐方法。因為現在系統往往記憶體很大,可以充分利用大記憶體,而共享記憶體更能實現資料併發訪問。
l 多執行緒(multi-threading)
現在基本上大部分軟體實現多執行緒或多程序,多執行緒對單CPU系統還只是順序利用CPU時間和改善使用者體驗,多CPU系統才是真正的並行。要注意的是多執行緒不要爭搶訪問同一資源而導致部分序列操作,要做到真正的並行操作多執行緒並不容易。另外,在多執行緒間同步一個龐大的資源,過多建立執行緒又沒有實現執行緒池也會導致系統性能下降。
l 負載平衡(load balancing)
物理上增加地位對等的叢集伺服器(Cluster),通過負載分配演算法分配相應伺服器來相應客戶端請求。很多系統支援負載均衡,Windows server2003 IIS就支援負載均衡服務,其他如WebLogic, WebSphere也有叢集版本支援負載均衡。當然你也可以自己實現負載分配演算法。
l 資料庫優化(database optimization)
如果應用程式使用了資料庫,可以採取許多步驟來消除訪問和寫入資料時的瓶頸:
Ø標識潛在的索引,但不要建立過多的索引。
Ø如果使用 SQL Server,則使用 SQL Server 的事件探查器和索引優化嚮導。
Ø監視處理器的使用;理想範圍是:75-80% 處理器時間。
Ø使用查詢分析器分析查詢計劃以優化查詢。
Ø使用儲存過程優化效能。
Ø標準化寫入的大量資料 —寫入較少的資料。
Ø取消標準化讀取的大量資料 —讀取較少的資料。
l 檔案系統優化
有時候系統性能不好,但當你關閉寫log的功能,效能一下子提高很多。因為頻繁的開啟關閉大log檔案時I/O開銷非常大,同樣記錄log到資料庫也一樣。所以,release版儘量減少寫log,或乾脆移到裸裝置上。
頻繁開啟關閉檔案對系統性能下降程度是驚人的,可以通過一些變通辦法來減少檔案的頻繁操作。
例如,原來的快取持久化實現是儲存在XML檔案,每次要獲得一個配置項,都開啟XML檔案,通過XPath拿到這個配置項的值,這樣效率不高,而且容易把這個XML檔案lock住;改進的方法是:通過比較XML檔案的修改時間(System.IO.File.GetLastWriteTime)判斷是否要再次開啟檔案,大大提高了效率;另一個可以改進的方法是:啟動時讀取所有配置到一個靜態的HashTable,每次要獲得一個配置項都從記憶體HashTable獲取,在最後或適當的時候持久化到XML。
l 程式碼效能設計
在程式設計實現上,程式碼效能設計也很重要,一些昂貴的操作會佔用大量的資源和CPU時間。例如,字串相加沒用StringBuilder, 頻繁建立物件,差勁的排序或遞迴演算法,過多的裝箱拆箱,過多的使用反射(Reflection),頻繁new HashTable或大的陣列,用異常(Catch Exception)用做正常的邏輯,使用複雜的正則表示式,等等。具體可以參考《Effective C++》《Effective
C#》等書籍。
l 語言的選擇
另外,語言選擇也很重要。比如相對於Java, C#, C++, 大多數OLTP系統用C語言效率高的多,因為在所有的高階程式設計語言中,C程式設計語言的執行效率是公認的。再比如我們熟悉的一些框架,框架本身是C#或是Java的,但其核心獨立模組是C++封裝的,這樣可以達到最佳的效能。所以對於一些特定的業務需求目標和資料的具體情況,對於核心的模組或演算法,可以用特定的語言來實現以獲得更好的效率。
l 應用層
比如應用層和資料庫的API,在.Net中就有就有DataReader、DataSet和IList等的選擇以及轉換等,這個根據具體情況而定;還有就是大家常採用的資料的格式化和壓縮,以及採用分頁,減少傳輸的資料量;是否可以把一部分處理邏輯放在客戶端呢,減少服務端的工作量。介面端也是有很多針對性能優化的考慮,例如繪圖,控制元件重繪都是非常耗資源的,各控制元件的資料載入和資料繫結效能也各不相同,儘量採用惰性載入,非同步載入;初始化和啟動速度等都是需要考慮和優化的。
這些只是我專案經驗積累和歸納,水平有限,專案有限,認識更有限,歡迎大家指正補充。
-------------
例1:
現象:廣告應用做效能測試時,用1個虛擬使用者時,效能正常,用3個虛擬使用者時響應有些慢,用10個虛擬使用者不到2分鐘,就報錯java.lang.OutOfMemoryError:Java heap space,負載方面正常
原因:每一次計數都要寫入,不停的開啟關閉檔案太頻繁,導致記憶體不足
解決方案:計數先存入快取,到一定量時再一次性寫入檔案,比如10000的量寫入
例2:
現象:我平時在linux環境上做開發時很習慣封裝類似如下的檔案追加函式,而且效能很高。
int appendfike(...)
{
fopen...
fwrite...
fclose...
...
}
按上面程式碼移植到vc6或vc2008時發現效能好低呀,追加上萬條記錄就感覺到很慢了。
但是我改成只打開一次,而寫N條記錄這樣效能卻很高。
請問是檔案系統不同的原因造成的嗎?還是其它原因呀?
原因:你這封裝的也太爛了,每一次計數都要寫入,不停的開啟關閉檔案太頻繁,不但導致效率很低,檔案本身就很容易壞掉而無法開啟。
解決方案:把FILE開啟就留下來重複使用;
先將要寫入的資料放到一塊記憶體中,積累到了一定數量的時候,一次寫入。幾乎所有的資料庫也都是這麼幹的,就是在commit(提交)的時候,資料才會真正寫入到資料庫檔案中,否則都是在記憶體裡面。在未commit之前,自己雖然可以看到資料表的變化,但是其它的使用者是看不到的。
曾經用sqlite3在android手機上做過一個實驗,如果每插入一條記錄就commit一次(類似樓主的做法),其效能要比插入300條記錄,commit一次差好幾十倍。
不管是*nix還是windows,樓主的那種做法,效率都不會高,硬碟也會受不了的。
轉:http://www.cnblogs.com/mainz/archive/2007/12/15/996082.html
留言:
Jonny Yu
博主主要談的是如何優化系統性能 ,其實我覺得這些技術本身都是很好的,但是一般來說出現效能問題並不是因為缺乏優化的意識和技術,而更多的是由於沒有發現的效能瓶頸或者忽視了這個瓶頸的重要性。 對於長期使用的系統的架構設計時,加入應用服務質量指標的檢測的目標是很有必要的
@Jonny Yu
同意,最好有效能測試,壓力測試,以及相關工具來找出瓶頸,這個往往比較困難