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

作者: 小洋(燕洋天)  來源: 博客園  發布時間: 2010-08-18 11:11  閱讀: 715 次  推薦: 0   原文鏈接   [收藏]  

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

  問題的描述

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

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

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

  如下圖: 

 

 

  第一個數據塊:

  采用鍵值對的形式:字典保存

  如果用戶請求到了49頁以后,那么就再次從數據庫中取出下一個數據塊(包含5011000數據),然后,現在內存中就有了1000條數據。

  至于緩存多久,數據什么失效,失效后怎么做,這里暫不談論。(網站在這種緩存策略下運行的很好)。 

  代碼如下:

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

  代碼的意思很清楚,從緩存中拿數據,如果緩存中沒有對應的數據,那么就先從數據庫中拿500條數據,然后放在緩存中,最后返回10條數據。

  后來,因為某些功能的需要,需要返回當前頁的前6頁數據和后6頁的數據,例如:如果當前頁是第12頁,那么就要返回12頁之前6Product(也就是第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,42126….); 

  上面傳入的是從第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>();然后分別把firstProductListsecondProductList遍歷,加入到resultProductList就行了,就這么簡單。

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

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

  今天就寫到了這里,啰嗦了這么多,希望對大家有一點點的幫助。

0
0
 
標簽:ASP.NET
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()