高性能ASP.NET站點(diǎn)構(gòu)建之細(xì)節(jié)決定成敗
高性能ASP.NET站點(diǎn)構(gòu)建系列文章目錄
- 高性能ASP.NET站點(diǎn)構(gòu)建之開篇
- 高性能ASP.NET站點(diǎn)構(gòu)建之剖析頁(yè)面的處理過(guò)程
- 高性能ASP.NET站點(diǎn)構(gòu)建之優(yōu)化HTTP請(qǐng)求
- 高性能ASP.NET站點(diǎn)構(gòu)建之細(xì)節(jié)決定成敗
- 高性能ASP.NET站點(diǎn)構(gòu)建之性能調(diào)優(yōu)綜述
- 高性能ASP.NET站點(diǎn)構(gòu)建之識(shí)別性能瓶頸
- 高性能ASP.NET站點(diǎn)構(gòu)建之簡(jiǎn)單的優(yōu)化措施
- ASP.NET站點(diǎn)構(gòu)建之減少不必要的請(qǐng)求
- 高性能ASP.NET站點(diǎn)構(gòu)建之托管資源優(yōu)化
- 高性能ASP.NET站點(diǎn)構(gòu)建之監(jiān)測(cè)CLR性能
前言:曾經(jīng)就因?yàn)橐粋€(gè)小小的疏忽,從而導(dǎo)致了服務(wù)器崩潰了,后來(lái)才發(fā)現(xiàn):原來(lái)就是因?yàn)橐粋€(gè)循環(huán)而導(dǎo)致的,所以,對(duì)“注意細(xì)節(jié)“這一說(shuō)法是深有感觸。
本篇的議題如下:
問(wèn)題的描述
細(xì)節(jié)的重要性
問(wèn)題的描述
首先,描述一下故事的背景:(希望大家耐心的故事讀完)
在網(wǎng)站中,網(wǎng)頁(yè)中的分頁(yè)控件每次顯示10條數(shù)據(jù),每次點(diǎn)擊下一頁(yè),就再次去取下一個(gè)10條數(shù)據(jù)。至于分頁(yè)的方法怎樣做,方法有很多,相信這點(diǎn)大家都知道。
過(guò)程是這樣的:在用戶請(qǐng)求數(shù)據(jù)的時(shí)候(考慮到了用戶的操作和網(wǎng)站的訪問(wèn)量)我會(huì)第一次取出500條數(shù)據(jù),然后把數(shù)據(jù)放在緩存中,也就是說(shuō),我取出了50頁(yè)的數(shù)據(jù),放在緩存中,這樣如果,以后用戶請(qǐng)求第一頁(yè)到第49頁(yè)的時(shí)候,就直接從緩存中拿數(shù)據(jù)。
如下圖:
第一個(gè)數(shù)據(jù)塊:
采用鍵值對(duì)的形式:字典保存
如果用戶請(qǐng)求到了49頁(yè)以后,那么就再次從數(shù)據(jù)庫(kù)中取出下一個(gè)數(shù)據(jù)塊(包含501到1000數(shù)據(jù)),然后,現(xiàn)在內(nèi)存中就有了1000條數(shù)據(jù)。
至于緩存多久,數(shù)據(jù)什么失效,失效后怎么做,這里暫不談?wù)摗#ňW(wǎng)站在這種緩存策略下運(yùn)行的很好)。
代碼如下:
- List<Product> products=GetDataFromCacheOrDatabase(condition,pageIndex,count….);
代碼的意思很清楚,從緩存中拿數(shù)據(jù),如果緩存中沒(méi)有對(duì)應(yīng)的數(shù)據(jù),那么就先從數(shù)據(jù)庫(kù)中拿500條數(shù)據(jù),然后放在緩存中,最后返回10條數(shù)據(jù)。
后來(lái),因?yàn)槟承┕δ艿男枰?,需要返回?dāng)前頁(yè)的前6頁(yè)數(shù)據(jù)和后6頁(yè)的數(shù)據(jù),例如:如果當(dāng)前頁(yè)是第12頁(yè),那么就要返回12頁(yè)之前6頁(yè)P(yáng)roduct(也就是第6,7,8,9,10,11頁(yè)的數(shù)據(jù)),和第12頁(yè)后的頁(yè)的Product(第13,14,15,16,17,18頁(yè)的數(shù)據(jù))。
如下:
當(dāng)然,如果當(dāng)前頁(yè)是第5頁(yè),那么就把之前所有5頁(yè)的數(shù)據(jù)都返回,另外再加上第5頁(yè)之后的6頁(yè)數(shù)據(jù)。
這里就可能涉及到跨塊獲取數(shù)據(jù),如:
如果當(dāng)前頁(yè)是第48頁(yè)的時(shí)候,那么返回前6頁(yè)數(shù)據(jù)是沒(méi)有什么問(wèn)題的,那么后6頁(yè)的數(shù)據(jù)就不足了,因?yàn)?9,40也得數(shù)據(jù)可以從緩存的數(shù)據(jù)塊中取到,至于51,52,53,54頁(yè)的數(shù)據(jù),就需要再次從數(shù)據(jù)庫(kù)中讀取,然后再次緩存(如果事先沒(méi)有被緩存)。
最后在緩存中的數(shù)據(jù)如下:
然后調(diào)用方法:(偽碼)
- List<Product> products=GetDataFromCacheOrDatabase(condition,42, 126….);
上面?zhèn)魅氲氖菑牡?2頁(yè)開始的數(shù)據(jù),也就是第48頁(yè)的前6頁(yè)和后6頁(yè)的數(shù)據(jù)。
這個(gè)方法的內(nèi)部實(shí)現(xiàn)是這樣的:
1. 首先從第一個(gè)數(shù)據(jù)塊中取出42頁(yè)到50頁(yè)的數(shù)據(jù)
取出數(shù)據(jù)后保存在一個(gè)List<Product> firstProductList;
2. 從第二個(gè)數(shù)據(jù)塊中取出從51頁(yè)到54頁(yè)(如果第二個(gè)數(shù)據(jù)塊在緩存不存在,就去數(shù)據(jù)庫(kù)中取501-1000條,然后再放在緩存的第二個(gè)數(shù)據(jù)塊中)。
保存在第二個(gè)List<Product> secondProductList
3. 然后把兩個(gè)list合并,返回結(jié)果。例如
- secondProductList.Foreach(u=>firstProductList.Add(u));
基本的實(shí)現(xiàn)就是這樣,看起來(lái)還行,也比較的合理,但是就是因?yàn)檫@個(gè)操作,從而導(dǎo)致服務(wù)器內(nèi)存溢出。
大家想想看是什么原因。
細(xì)節(jié)的重要性
其實(shí)緩存的數(shù)據(jù)不是很多,是不足以讓服務(wù)器內(nèi)存溢出的,但是服務(wù)器還是出現(xiàn)了out of memory的異常。之前一直跑的很好,就是改了代碼之后才出現(xiàn)問(wèn)題的。
其實(shí)這就是由于一個(gè)最基本的錯(cuò)誤產(chǎn)生的:引用類型。
下面就來(lái)分析下:
首先是從第一個(gè)數(shù)據(jù)塊中取出數(shù)據(jù),然后用
List<Product> firstProductList 引用指向取出的數(shù)據(jù)
然后從第二個(gè)數(shù)據(jù)塊中取出數(shù)據(jù),用
List<Product> secondProductList指向數(shù)據(jù)的引用
如下圖
在第三步中采用
- secondProductList.Foreach(u=>firstProductList.Add(u));
把secondProductList中的數(shù)據(jù)加入到firstProductList中,就因?yàn)槭且妙愋?,其?shí)實(shí)際操作的結(jié)果是:不斷的在改變第一個(gè)數(shù)據(jù)塊中的數(shù)據(jù),使得第一個(gè)數(shù)據(jù)塊中的數(shù)據(jù)逐漸的變多。
現(xiàn)在當(dāng)前頁(yè)是48頁(yè),采用上面的操作,致使第一個(gè)數(shù)據(jù)塊中的數(shù)據(jù)增加了60條,
如果用戶再次翻頁(yè),到了49頁(yè),那么第一個(gè)數(shù)據(jù)塊中的數(shù)據(jù)又增多了60條
依此類推,最后導(dǎo)致了服務(wù)器內(nèi)存的不足,致使服務(wù)器崩潰了。原本的“功臣”----緩存卻成為了罪魁禍?zhǔn)住?/p>
其實(shí)這個(gè)問(wèn)題的解決,只要改變一點(diǎn)點(diǎn)的代碼就行了:
- List<Product> firstProductList;
- List<Product> secondProductList;
然后
List<Product> resultProductList=new List<Product>();然后分別把firstProductList,secondProductList遍歷,加入到resultProductList就行了。
就這么簡(jiǎn)單。
一個(gè)小的細(xì)節(jié),導(dǎo)致了大的問(wèn)題。
原文鏈接:http://www.cnblogs.com/yanyangtian/archive/2010/08/17/1801129.html
【編輯推薦】