軟件系統性能瓶頸真相


請看一個測試:

1、快速排序100次,然后計算排序一次所需要的時間。

public QuickSort() {  
        long beginTime = System.nanoTime();  
        //排100遍  
        int b[]=a.clone();  
        for(int i=0;i<100;i++){  
            quick(b);  
        }  
        long total = System.nanoTime() - beginTime;  
        //18133  
        System.out.println("小數組快速排序所需平均時間:" + total/100);  
    }  

在我的機器上,大概時間是:

小數組快速排序所需平均時間:18133

 

 

2、計算使用system.out.println打印一個字符串使用的時間:

public static void main(String[] args) {  
        long beginTime = System.nanoTime();  
        for(int i=0;i<100;i++){  
            System.out.println("hello World");  
        }  
        long total = System.nanoTime() - beginTime;  
        /** 
         * 18214 
         */  
        System.out.println("平均每次磁盤IO時間:" + total/100);  
    }  

打印時間大概是:

平均每次磁盤IO時間:18334

 

 

3、記錄從局域網的機器上獲取一個數據所需要的時間,我們使用虛擬機的memcached作為測試對象。

public class MemcachedTest {  
    private MemcachedClient memCachedClient ;  
    MemcachedTest() throws IOException{  
        memCachedClient = new MemcachedClient(  
                new InetSocketAddress("192.168.1.103", 11211));  
    }  
      
    public static void main(String[] args) throws IOException {  
        new MemcachedTest().test();  
    }  
  
    private void test() throws IOException {  
        long beginTime = System.nanoTime();  
        for(int i=0;i<100;i++){  
            memCachedClient.get("dfsdf" + i);  
        }  
        long totalTime = System.nanoTime()-beginTime;  
        //計算平均時間,這里抹掉了小數點,平均:516486  
        System.out.println("從局域網的memcached取一次數據所需平均時間:" + (totalTime/100));  
    }  
}  

打印時間:

從局域網的memcached取一次數據所需平均時間:528065

 

其實測試1是計算機最擅長的操作,通常這個操作在計算機的內存進行,測試2操作其實是一個IO操作,會涉及到一個向顯卡寫數據的IO,第三個操作,其實是一個網絡IO,計算機會向網卡發送一個指令,到另外一條機器接收到命令,最后再從網卡讀結果。

最后得出,各時間大概有如下關系:

快速排序≈一次磁盤IO≈1/29memcached所需要的時間。

 

可以看出,系統中一旦發生IO,其所需的時間通常比直接操作內存慢好幾百倍,因為一個快速排序,會有好幾百次的數據移動和比較。對於經過網絡的操作,通常其速度又會比磁盤慢至少一個數量級。

 

盡管現在各種分布式產品很流行,但是這些基礎常識,還是應該牢記在心,這樣才能夠寫出高效率,高穩定性的軟件。下面是我總結出的代碼中幾條涉及到性能的幾條准則:

一、能夠在內存解決的盡量不要訪問磁盤,能在本機磁盤解決的,盡量不要訪問網絡。

二、多線程並發訪問程序中,能不共享數據的盡量不要共享(StringBuilder優於StringBuffer);能夠使用並發工具類(Atomic相關類,ConcurrentHashMap,CAS等)的,盡量不要使用synchronized。

三、在訪問數據庫時,對於常用的查詢條件一定要建好復合索引。能夠不排序的盡量不要排序,如果需要排序的,一定把排序字段提前存好,而不要在查詢的時候再計算排序字段。

四、批量優於單個操作,很多產品,比如數據庫,memcached等,都有批量及單個操作。在一些任務類中,如果能夠批量操作,則優先使用批量接口。

五、某些計算時間長(比如用戶要導出大文件)的操作,盡量進行優化。如果系統中必須存在這樣的操作,那就用隊列來專門解決這些問題,一定要控制系統線程的數量。

 

另外,在項目中,大多數系統的瓶頸可能都在數據庫,通常,如果一個系統的用戶超過1w,緩存就非常必要了。其實,性能問題,不僅僅出在代碼級別,還有可能是在中間件上發生,比如apache的默認配置,就非常不適合大的並發系統,mysql的緩存,oracle的最大連接數等等,甚至前段的JS加載及運行,都可能帶來性能問題。精通一些中間件,同樣可以提高系統的性能和吞吐量。

 

通常性能都是在用戶使用時才被發現,因此,排查問題比較困難。關於他的話題,寫出好幾本書也闡述不完,在這里,寫的標題有點誇張,但是,不可置疑,軟件中的IO確實是系統瓶頸很重要的一個原因。

 

 

對這類話題感興趣?歡迎發送郵件至 donlianli@126.com
關於我:邯鄲人,擅長Java,Javascript,Extjs,oracle sql。
更多我之前的文章,可以訪問  我的空間


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM