【原創】構建高效能ASP.NET網站之三 細節決定成敗

來源:互聯網
上載者:User

構建高效能ASP.NET網站之三 細節決定成敗

 

  前言:曾經就因為一個小小的疏忽,從而導致了伺服器崩潰了,後來才發現:原來就是因為一個迴圈而導致的,所以,對“注意細節“這一說法是深有感觸。

 

 

  系列文章連結:

  構建高效能ASP.NET網站 開篇

  構建高效能ASP.NET網站之一 剖析頁面的處理過程(前端)

  構建高效能ASP.NET網站之二 最佳化HTTP請求(前端)

  構建高效能ASP.NET網站之三 細節決定成敗

  構建高效能ASP.NET網站 第五章—效能調優綜述(前篇)

  大型高效能ASP.NET系統架構設計  

  構建高效能ASP.NET網站 第五章—效能調優綜述(中篇)

  構建高效能ASP.NET網站 第五章—效能調優綜述(後篇)  

  構建高效能ASP.NET網站 第六章—效能瓶頸診斷與初步調優(上篇)—識別效能瓶頸

  構建高效能ASP.NET網站 第六章—效能瓶頸診斷與初步調優(下前篇)—簡單的最佳化措施

  構建高效能ASP.NET網站 第六章—效能瓶頸診斷與初步調優(下後篇)—減少不必要的請求

  構建高效能ASP.NET網站 第七章 如何解決記憶體的問題(前中篇)—託管資源最佳化—監測CLR效能

  構建高效能ASP.NET網站 第七章 如何解決記憶體的問題(前篇)—託管資源最佳化—記憶體回收機制深度剖析

  構建高效能ASP.NET網站 第七章 如何解決記憶體的問題(前中篇)—託管資源最佳化—監測CLR效能

 

  

 

 

本篇的議題如下:

問題的描述

細節的重要性

 

  問題的描述

 

  首先,描述一下故事的背景:(希望大家耐心的故事讀完)

  在網站中,網頁中的分頁控制項每次顯示10條資料,每次點擊下一頁,就再次去取下一個10條資料。至於分頁的方法怎樣做,方法有很多,相信這點大家都知道。

 

  過程是這樣的:在使用者請求資料的時候(考慮到了使用者的操作和網站的訪問量)我會第一次取出500條資料,然後把資料放在緩衝中,也就是說,我取出了50頁的資料,放在緩衝中,這樣如果,以後使用者請求第一頁到第49頁的時候,就直接從緩衝中拿資料。

  如:

  

 

  第一個資料區塊:

 

  採用索引值對的形式:字典儲存

 

 

  如果使用者請求到了49頁以後,那麼就再次從資料庫中取出下一個資料區塊(包含501到1000資料),然後,現在記憶體中就有了1000條資料。

 

  至於緩衝多久,資料什麼失效,失效後怎麼做,這裡暫不談論。(網站在這種緩衝策略下啟動並執行很好)。 

  代碼如下:

 

List<Product> products=GetDataFromCacheOrDatabase(condition,pageIndex,count….);

 

 

  代碼的意思很清楚,從緩衝中拿資料,如果緩衝中沒有對應的資料,那麼就先從資料庫中拿500條資料,然後放在緩衝中,最後返回10條資料。

 

  後來,因為某些功能的需要,需要返回當前頁的前6頁資料和後6頁的資料,例如:如果當前頁是第12頁,那麼就要返回12頁之前6頁Product(也就是第6,7,8,9,10,11頁的資料),和第12頁後的頁的Product(第13,14,15,16,17,18頁的資料)。 

  如下:

  

 

 

  當然,如果當前頁是第5頁,那麼就把之前所有5頁的資料都返回,另外再加上第5頁之後的6頁資料。

 

  這裡就可能涉及到跨塊擷取資料,如:

  如果當前頁是第48頁的時候,那麼返回前6頁資料是沒有什麼問題的,那麼後6頁的資料就不足了,因為49,40也得資料可以從緩衝的資料區塊中取到,至於51,52,53,54頁的資料,就需要再次從資料庫中讀取,然後再次緩衝(如果事先沒有被緩衝)。

 

 

  最後在緩衝中的資料如下:

 

   

  然後調用方法:(偽碼)

 

 

List<Product> products=GetDataFromCacheOrDatabase(condition,42, 126….); 

 

 

  上面傳入的是從第42頁開始的資料,也就是第48頁的前6頁和後6頁的資料。

  這個方法的內部實現是這樣的:

    1.    首先從第一個資料區塊中取出42頁到50頁的資料

    取出資料後儲存在一個List<Product> firstProductList;

    2.    從第二個資料區塊中取出從51頁到54頁(如果第二個資料區塊在緩衝不存在,就去資料庫中取501-1000條,然後再放在緩衝的第二個資料區塊中)。

    儲存在第二個List<Product> secondProductList

    3. 然後把兩個list合并,返回結果。例如

           secondProductList.Foreach(u=>firstProductList.Add(u));

 

  基本的實現就是這樣,看起來還行,也比較的合理,但是就是因為這個操作,從而導致伺服器記憶體溢出。

  大家想想看是什麼原因。

 

  細節的重要性

 

  其實緩衝的資料不是很多,是不足以讓伺服器記憶體溢出的,但是伺服器還是出現了out of memory的異常。之前一直跑的很好,就是改了代碼之後才出現問題的。

 

  其實這就是由於一個最基本的錯誤產生的:參考型別。

  下面就來分析下:

  首先是從第一個資料區塊中取出資料,然後用

    List<Product> firstProductList 引用指向取出的資料

  然後從第二個資料區塊中取出資料,用

    List<Product> secondProductList指向資料的引用

 

  如

 

 

 

  在第三步中採用

 

    secondProductList.Foreach(u=>firstProductList.Add(u));

  把secondProductList中的資料加入到firstProductList中,就因為是參考型別,其實實際操作的結果是:不斷的在改變第一個資料區塊中的資料,使得第一個資料區塊中的資料逐漸的變多。

  現在當前頁是48頁,採用上面的操作,致使第一個資料區塊中的資料增加了60條,

  如果使用者再次翻頁,到了49頁,那麼第一個資料區塊中的資料又增多了60條

  依此類推,最後導致了伺服器記憶體的不足,致使伺服器崩潰了。原本的“功臣”----緩衝卻成為了罪魁禍首。

 

  其實這個問題的解決,只要改變一點點的代碼就行了:

    List<Product> firstProductList;

    List<Product> secondProductList;

  然後

  List<Product> resultProductList=new List<Product>();然後分別把firstProductList,secondProductList遍曆,加入到resultProductList就行了。

就這麼簡單。

 

  一個小的細節,導致了大的問題。 

  

  不要忽視每一個細節。不要做沒有比較的迴圈等操作,一定要審視代碼,重構代碼。 

  今天就寫到了這裡,囉嗦了這麼多,希望對大家有一點點的協助。

  如有不正確,或者認為不妥的地方,希望大家斧正!而且留下高見!謝謝!

 

著作權為小洋和部落格園所有,歡迎轉載,轉載請標明出處給作者。

   http://www.cnblogs.com/yanyangtian

 

相關文章

聯繫我們

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

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

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.