優化你的DiscuzNT,讓它跑起來
去年用DiscuzNT3.0做過二次開發,做過一些性能優化,但是時間關系一直沒機會寫下來;趁著5.1長假,來寫篇回憶性的隨筆吧。
之前看過園子里代震軍同學的博客,知道了老代同學是DiscuzNT團隊的一員,從他的博文學了不少東西 ,我這里寫的博文是針對一些問題提出自己的看發和解決方案,針對問題并無針對任何人之意,秉著技術交流的原則。
DiscuzNT給我的印象是
1)功能很強大,所有你能想到的基本都已經有了;
2)性能有待優化,數據量較大的情況會產生性能瓶頸(這也正是寫此文的目的)。之前發的博文由于缺乏經驗,沒有足夠的論據,今天會多提供些圖文并茂的論據。
好了,言歸正轉,開始我們今天的優化之旅。
本系統環境如下:
軟件環境:DiscuzNT3.0 , sqlserver2000,windwos server 2003
數據環境:主貼表 dnt_topics 約220萬條記錄,回復表3個,dnt_posts1 約400萬, dnt_posts2 約500萬, dnt_posts3 約500萬,附件表 約170萬,用戶表 dnt_users 約20萬, 論壇表 dnt_forums 約5000個論壇
現象:看帖時,如果帖子包含附件,會很卡;
目的:優化看帖速度,尤其是有附件的情況
動手:看下它是如何獲取附件的,找到showtopic.aspx.cs,代碼如下:
- postlist = Posts.GetPostList(postpramsInfo, out attachmentlist, ismoder == 1);
再看下 Posts.GetPostList() 方法的代碼:
- /// <summary>
- /// 獲取指定條件的帖子DataSet
- /// </summary>
- /// <param name="_postpramsinfo">參數列表</param>
- /// <returns>指定條件的帖子DataSet</returns>
- public static List<ShowtopicPagePostInfo> GetPostList(PostpramsInfo postpramsInfo, out List<ShowtopicPageAttachmentInfo> attachList, bool isModer)
- {
- List<ShowtopicPagePostInfo> postList = Data.Posts.GetPostList(postpramsInfo);
- int adCount = Advertisements.GetInPostAdCount("", postpramsInfo.Fid);
- foreach (ShowtopicPagePostInfo postInfo in postList)
- {
- LoadExtraPostInfo(postInfo, adCount);
- }
- attachList = new List<ShowtopicPageAttachmentInfo>();
- if (postList.Count == 0)
- return postList;
- string pidList = GetPidListWithAttach(postList);
- attachList = Attachments.GetAttachmentList(postpramsInfo, pidList);
- ParsePostListExtraInfo(postpramsInfo, attachList, isModer, postList);
- return postList;23 }
從這里可以看出,DiscuzNT是把所有的帖子id組裝成 “ id1,id2,id3,id4 ” 的形式,然后傳入數據庫,避免多次調用數據庫,這個思路很好,現在我們順藤摸瓜,看看它調用了數據庫的腳本,它調用了這個過程 dnt_getattachmentlistbypid, 用profiler跟蹤這個過程看看性能。
看上面的圖,exec dnt_getattachmentlistbypid @pidlist = '5163797' 這個腳本的cpu=4531,reads=152641,duration=6156,很可觀吧,如果同時有10個人來調用這個過程,估計數據庫的壓力就大了,如果100人,難以想象。那我們怎么來優化這個過程呢,先看看里面它怎么寫的,是否用到了索引。
- ALTER PROCEDURE [dnt_getattachmentlistbypid]
- @pidlist varchar(500)
- AS
- SELECT
- [aid],
- [uid],
- [tid],
- [pid],
- [postdatetime],
- [readperm],
- [filename],
- [description],
- [filetype],
- [filesize],
- [attachment],
- [downloads],
- [attachprice],
- [width],
- [height]
- FROM [dnt_attachments]
- WHERE CHARINDEX(','+RTRIM([dnt_attachments].[pid])+',', ','+@pidlist+',')>0GO
這里主要查找的條件是pid,如果在pid列上建立索引,并且過程能用到索引,效果應該會更理想,這個優化工作我分為如下幾步:
1)pid列上是否有索引;
2)過程是否用到了索引;
3)優化sql腳本;
4)跟蹤優化后效果;
我們一步一個坑往下走:
1)sp_helpindex dnt_attachments 看看是否有索引,如下圖,從圖中可以看到pid列上是有索引的,如果沒有索引,請建立相關索引
2)看看是否用到了索引,CTRL + L 看看下面語句的執行計劃,他用到的索引是 PK_dnt_attachments,根本沒用到我們期望的pid
3)沒用到我們期望的索引,那我們就來優化一下;上面的dnt_getattachmentlistbypid過程里面 WHERE CHARINDEX(','+RTRIM([dnt_attachments].[pid])+',', ','+@pidlist+',')>0 對pid進行了列運算,這個是罪魁禍首,我們想辦法把這個列運算去掉,這個過程最終改成下面這個樣子:
- ALTER PROCEDURE [dnt_getattachmentlistbypid]
- @pidlist varchar(500)
- AS
- declare @sql nvarchar(2000)
- set @sql = '
- SELECT
- [aid],
- [uid],
- [tid],
- [pid],
- [postdatetime],
- [readperm],
- [filename],
- [description],
- [filetype],
- [filesize],
- [attachment],
- [downloads],
- [attachprice],
- [width],
- [height]
- FROM [dnt_attachments]
- WHERE pid in (' + @pidlist + ')'
- exec(@sql)
- GO
4)改完之后我們來跟蹤下優化后的性能,看看跟蹤效果圖(同一個過程,同一個參數,第2個是優化前,第4個是優化后,優化效果灰常滿意)
至此,我們的優化告一段落。
原文鏈接:http://www.cnblogs.com/gezifeiyang/archive/2011/05/02/2034124.html
【編輯推薦】