構建高性能ASP.NET站點之三 細節決定成敗
前言:曾經就因為一個小小的疏忽,從而導致了服務器崩潰了,后來才發現:原來就是因為一個循環而導致的,所以,對“注意細節“這一說法是深有感觸。
問題的描述
首先,描述一下故事的背景:(希望大家耐心的故事讀完)
在網站中,網頁中的分頁控件每次顯示10條數據,每次點擊下一頁,就再次去取下一個10條數據。至于分頁的方法怎樣做,方法有很多,相信這點大家都知道。
過程是這樣的:在用戶請求數據的時候(考慮到了用戶的操作和網站的訪問量)我會第一次取出500條數據,然后把數據放在緩存中,也就是說,我取出了50頁的數據,放在緩存中,這樣如果,以后用戶請求第一頁到第49頁的時候,就直接從緩存中拿數據。
如下圖:
第一個數據塊:
采用鍵值對的形式:字典保存
如果用戶請求到了49頁以后,那么就再次從數據庫中取出下一個數據塊(包含501到1000數據),然后,現在內存中就有了1000條數據。
至于緩存多久,數據什么失效,失效后怎么做,這里暫不談論。(網站在這種緩存策略下運行的很好)。
代碼如下:
代碼的意思很清楚,從緩存中拿數據,如果緩存中沒有對應的數據,那么就先從數據庫中拿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頁的數據,就需要再次從數據庫中讀取,然后再次緩存(如果事先沒有被緩存)。
最后在緩存中的數據如下:
然后調用方法:(偽碼)
上面傳入的是從第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就行了,就這么簡單。
一個小的細節,導致了大的問題。
不要忽視每一個細節。不要做沒有比較的循環等操作,一定要審視代碼,重構代碼。
今天就寫到了這里,啰嗦了這么多,希望對大家有一點點的幫助。