优化你的DiscuzNT3.0,让它跑起来(2)发帖回帖篇

注:本文仅针对 DiscuzNT3.0, sqlserver 2000版本,其他版本请勿对号入座.

上次对DiscuzNT的看帖做了性能优化,这次主要针对发帖和回帖做下优化,本文还是以优化数据库为主,C#的优化以后有时间另外记录。

看帖,发帖,回帖 都是一个论坛的基本操作,如果性能不好导致这些操作变慢,论坛的效果可想而知了,那我们来看看DiscuzNT发帖和回帖都有些什么操作,性能如何。

做事情总要有个先后,我给这次的优化工作也做了个步骤:(有图有真相)

1)看代码,查到发帖回帖调用的存储过程;

发帖调用了两个过程,代码如下:

  public   int  CreateTopic(TopicInfo topicInfo)
{
    DbParameter[] parms 
=  {
    DbHelper.MakeInParam(
" @fid " , (DbType)SqlDbType.SmallInt,  2 , topicInfo.Fid), 
    DbHelper.MakeInParam(
" @iconid " , (DbType)SqlDbType.SmallInt,  2 , topicInfo.Iconid), 
    DbHelper.MakeInParam(
" @title " , (DbType)SqlDbType.NChar,  60 , topicInfo.Title), 
    DbHelper.MakeInParam(
" @typeid " , (DbType)SqlDbType.SmallInt,  2 , topicInfo.Typeid), 
    DbHelper.MakeInParam(
" @readperm " , (DbType)SqlDbType.Int,  4 , topicInfo.Readperm), 
    DbHelper.MakeInParam(
" @price " , (DbType)SqlDbType.SmallInt,  2 , topicInfo.Price), 
    DbHelper.MakeInParam(
" @poster " , (DbType)SqlDbType.NChar,  15 , topicInfo.Poster), 
    DbHelper.MakeInParam(
" @posterid " , (DbType)SqlDbType.Int,  4 , topicInfo.Posterid), 
    DbHelper.MakeInParam(
" @postdatetime " , (DbType)SqlDbType.SmallDateTime, 4 , DateTime.Parse(topicInfo.Postdatetime)), 
    DbHelper.MakeInParam(
" @lastpost " , (DbType)SqlDbType.VarChar,  0 , topicInfo.Lastpost), 
    DbHelper.MakeInParam(
" @lastpostid " , (DbType)SqlDbType.Int,  4 , topicInfo.Lastpostid),
    DbHelper.MakeInParam(
" @lastposter " , (DbType)SqlDbType.NChar,  15 , topicInfo.Lastposter), 
    DbHelper.MakeInParam(
" @views " , (DbType)SqlDbType.Int,  4 , topicInfo.Views), 
    DbHelper.MakeInParam(
" @replies " , (DbType)SqlDbType.Int,  4 , topicInfo.Replies), 
    DbHelper.MakeInParam(
" @displayorder " , (DbType)SqlDbType.Int,  4 , topicInfo.Displayorder), 
    DbHelper.MakeInParam(
" @highlight " , (DbType)SqlDbType.VarChar,  500 , topicInfo.Highlight), 
    DbHelper.MakeInParam(
" @digest " , (DbType)SqlDbType.Int,  4 , topicInfo.Digest), 
    DbHelper.MakeInParam(
" @rate " , (DbType)SqlDbType.Int,  4 , topicInfo.Rate), 
    DbHelper.MakeInParam(
" @hide " , (DbType)SqlDbType.Int,  4 , topicInfo.Hide), 
    DbHelper.MakeInParam(
" @attachment " , (DbType)SqlDbType.Int,  4 , topicInfo.Attachment), 
    DbHelper.MakeInParam(
" @moderated " , (DbType)SqlDbType.Int,  4 , topicInfo.Moderated), 
    DbHelper.MakeInParam(
" @closed " , (DbType)SqlDbType.Int,  4 , topicInfo.Closed),
    DbHelper.MakeInParam(
" @magic " , (DbType)SqlDbType.Int,  4 , topicInfo.Magic),
    DbHelper.MakeInParam(
" @special " , (DbType)SqlDbType.TinyInt,  1 , topicInfo.Special),
    DbHelper.MakeInParam(
" @attention " , (DbType)SqlDbType.Int,  4 , topicInfo.Attention)
        };
    
return  TypeConverter.ObjectToInt(DbHelper.ExecuteDataset(CommandType.StoredProcedure,
    
string .Format( " {0}createtopic " , BaseConfigs.GetTablePrefix),
    parms).Tables[
0 ].Rows[ 0 ][ 0 ],  - 1 );

 

 

  /// <summary>
///  创建帖子
///   </summary>
///   <param name="postinfo"> 帖子信息类 </param>
///   <returns> 返回帖子id </returns>
public   int  CreatePost(PostInfo postInfo,  string  postTableId)
{
    DbParameter[] parms 
=  {
    DbHelper.MakeInParam(
" @fid " ,(DbType)SqlDbType.SmallInt, 2 ,postInfo.Fid),
    DbHelper.MakeInParam(
" @tid " ,(DbType)SqlDbType.Int, 4 ,postInfo.Tid),
    DbHelper.MakeInParam(
" @parentid " ,(DbType)SqlDbType.Int, 4 ,postInfo.Parentid),
    DbHelper.MakeInParam(
" @layer " ,(DbType)SqlDbType.Int, 4 ,postInfo.Layer),
    DbHelper.MakeInParam(
" @poster " ,(DbType)SqlDbType.VarChar, 15 ,postInfo.Poster),
    DbHelper.MakeInParam(
" @posterid " ,(DbType)SqlDbType.Int, 4 ,postInfo.Posterid),
    DbHelper.MakeInParam(
" @title " ,(DbType)SqlDbType.NVarChar, 60 ,postInfo.Title),
    DbHelper.MakeInParam(
" @topictitle " ,(DbType)SqlDbType.NVarChar, 60 ,postInfo.Topictitle),
    DbHelper.MakeInParam(
" @postdatetime " ,(DbType)SqlDbType.SmallDateTime, 4 , DateTime.Parse(postInfo.Postdatetime)),
    DbHelper.MakeInParam(
" @message " ,(DbType)SqlDbType.NText, 0 ,postInfo.Message),
    DbHelper.MakeInParam(
" @ip " ,(DbType)SqlDbType.VarChar, 15 ,postInfo.Ip),
    DbHelper.MakeInParam(
" @lastedit " ,(DbType)SqlDbType.NVarChar, 50 ,postInfo.Lastedit),
    DbHelper.MakeInParam(
" @invisible " ,(DbType)SqlDbType.Int, 4 ,postInfo.Invisible),
    DbHelper.MakeInParam(
" @usesig " ,(DbType)SqlDbType.Int, 4 ,postInfo.Usesig),
    DbHelper.MakeInParam(
" @htmlon " ,(DbType)SqlDbType.Int, 4 ,postInfo.Htmlon),
    DbHelper.MakeInParam(
" @smileyoff " ,(DbType)SqlDbType.Int, 4 ,postInfo.Smileyoff),
    DbHelper.MakeInParam(
" @bbcodeoff " ,(DbType)SqlDbType.Int, 4 ,postInfo.Bbcodeoff),
    DbHelper.MakeInParam(
" @parseurloff " ,(DbType)SqlDbType.Int, 4 ,postInfo.Parseurloff),
    DbHelper.MakeInParam(
" @attachment " ,(DbType)SqlDbType.Int, 4 ,postInfo.Attachment),
    DbHelper.MakeInParam(
" @rate " ,(DbType)SqlDbType.SmallInt, 2 ,postInfo.Rate),
    DbHelper.MakeInParam(
" @ratetimes " ,(DbType)SqlDbType.Int, 4 ,postInfo.Ratetimes)
       };
    
return  TypeConverter.ObjectToInt(DbHelper.ExecuteScalar(CommandType.StoredProcedure,
    
string .Format( " {0}createpost{1} " , BaseConfigs.GetTablePrefix, postTableId),
    parms), 
- 1 );

 

有两个主要方法,1个是CreateTopic(),对应调用dnt_createtopic存储过程,这个是把主贴的基本信息保存到dnt_topics表,其中不包含帖子内容;

1个是CreatePost()对应调用dnt_createposts{0},{0}是分表名称,是把帖子的所有信息保存到dnt_posts{0}表,此表帖子的详细信息(包括回复也在此表);

论坛展示帖子列表的时候查询的是dnt_topics表的信息,帖子里面展示各楼层信息的时候查询的是dnt_posts{0}表。

 

2)发一个帖或者回复一个帖子,看看存储过程的性能 ; 

我们用profiler跟踪一下这两个过程的性能如何,看图: 

 优化你的DiscuzNT3.0,让它跑起来(2)发帖回帖篇_第1张图片

从上图看到 dnt_createposts3这个过程的reads比较高,我们看看它是怎么写的,有没有优化的可能。

 

3)查看存储过程的sql写法, 如果发现问题,指出问题; 

dnt_createposts3这个过程的脚本如下:

ALTER        PROCEDURE  dnt_createpost3
@fid   int ,
@tid   int ,
@parentid   int ,
@layer   int ,
@poster   varchar ( 20 ),
@posterid   int ,
@title   nvarchar ( 60 ),
@topictitle   nvarchar ( 60 ),
@postdatetime   char ( 20 ),
@message   ntext ,
@ip   varchar ( 15 ),
@lastedit   varchar ( 50 ),
@invisible   int ,
@usesig   int ,
@htmlon   int ,
@smileyoff   int ,
@bbcodeoff   int ,
@parseurloff   int ,
@attachment   int ,
@rate   int ,
@ratetimes   int

AS

DEClARE   @postid   int

DELETE   FROM   [ dnt_postid ]   WHERE   DATEDIFF (n, postdatetime,  GETDATE ())  > 5

INSERT   INTO   [ dnt_postid ]  ( [ postdatetime ] VALUES ( GETDATE ())

SELECT   @postid = SCOPE_IDENTITY ()

INSERT   INTO   [ dnt_posts3 ] ( [ pid ] [ fid ] [ tid ] [ parentid ] [ layer ] [ poster ] ,
    
[ posterid ] [ title ] [ postdatetime ] [ message ] [ ip ] [ lastedit ]
    
[ invisible ] [ usesig ] [ htmlon ] [ smileyoff ] [ bbcodeoff ] [ parseurloff ] ,
    
[ attachment ] [ rate ] [ ratetimes ]
    
VALUES ( @postid @fid @tid @parentid @layer @poster @posterid @title ,
    
@postdatetime @message @ip @lastedit @invisible @usesig @htmlon @smileyoff ,
    
@bbcodeoff @parseurloff @attachment @rate @ratetimes )

IF   @parentid = 0
BEGIN
    
UPDATE   [ dnt_posts3 ]   SET   [ parentid ] = @postid   WHERE   [ pid ] = @postid
END

IF   @@ERROR = 0
BEGIN
    
IF    @invisible   =   0
    
BEGIN
        
UPDATE   [ dnt_statistics ]   SET   [ totalpost ] = [ totalpost ]   +   1

        
DECLARE   @fidlist   AS   VARCHAR ( 1000 )
        
DECLARE   @strsql   AS   VARCHAR ( 4000 )
            
        
SET   @fidlist   =   '' ;
            
        
SELECT   @fidlist   =   ISNULL ( [ parentidlist ] , '' FROM   [ dnt_forums ]   WHERE   [ fid ]   =   @fid

        
IF   RTRIM ( @fidlist ) <> ''
        
BEGIN
            
SET   @fidlist   =   RTRIM ( @fidlist +   ' , '   +   CAST ( @fid   AS   VARCHAR ( 10 ))
        
END
        
ELSE
        
BEGIN
            
SET   @fidlist   =   CAST ( @fid   AS   VARCHAR ( 10 ))
        
END
            
        
--  性能隐患,此sql语句进行了列运算
         UPDATE   [ dnt_forums ]   SET       [ posts ] = [ posts ]   +   1
            
[ todayposts ] = CASE  
            
WHEN   DATEDIFF ( day [ lastpost ] GETDATE ()) = 0  
            
THEN   [ todayposts ] * 1   +   1  
            
ELSE   1  
            
END ,
            
[ lasttid ] = @tid ,    
            
[ lasttitle ] = @topictitle ,
            
[ lastpost ] = @postdatetime ,
            
[ lastposter ] = @poster ,
            
[ lastposterid ] = @posterid                              
            
WHERE  ( CHARINDEX ( ' , '   +   RTRIM ( [ fid ] +   ' , ' ' , '
            
+  ( SELECT   @fidlist   AS   [ fid ] +   ' , ' >   0
            
            
        
UPDATE   [ dnt_users ]   SET
                
[ lastpost ]   =   @postdatetime ,
                
[ lastpostid ]   =   @postid ,
                
[ lastposttitle ]   =   @title ,
                
[ posts ]   =   [ posts ]   +   1 ,
                
[ lastactivity ]   =   GETDATE ()
            
WHERE   [ uid ]   =   @posterid
        
        
        
IF   @layer <= 0
        
BEGIN
            
UPDATE   [ dnt_topics ]   SET   [ replies ] = 0 , [ lastposter ] = @poster ,
                
[ lastpost ] = @postdatetime , [ lastposterid ] = @posterid  
                
WHERE   [ tid ] = @tid
        
END
        
ELSE
        
BEGIN
            
UPDATE   [ dnt_topics ]   SET   [ replies ] = [ replies ]   +   1 , [ lastposter ] = @poster ,
                
[ lastpost ] = @postdatetime , [ lastposterid ] = @posterid
                
WHERE   [ tid ] = @tid
        
END
    
END

    
UPDATE   [ dnt_topics ]   SET   [ lastpostid ] = @postid   WHERE   [ tid ] = @tid

    
IF   @posterid   <>   - 1
    
BEGIN
        
INSERT   [ dnt_myposts ] ( [ uid ] [ tid ] [ pid ] [ dateline ]
            
VALUES ( @posterid @tid @postid @postdatetime )
    
END
    
END
    
SELECT   @postid   AS postid

GO 

 这个过程比较长,不过存在性能隐患的脚本上面已经注明,就是update forums 这句,dnt_forums 数据量越大(我们现在有3000个论坛), 更新这个表所花的时间越多,因为它进行了列运算 WHERE (CHARINDEX(',' + RTRIM([fid]+ ','',' + (SELECT @fidlist AS [fid]+ ','> 0,用不到索引。

 

4)优化,测试优化的结果。

 

优化的方法有多种,下面我给出一种优化的方案,使update dnt_forums 这个操作用到索引,优化后的过程如下:

ALTER                PROCEDURE  dnt_createpost3
@fid   int ,
@tid   int ,
@parentid   int ,
@layer   int ,
@poster   varchar ( 20 ),
@posterid   int ,
@title   nvarchar ( 60 ),
@topictitle   nvarchar ( 60 ),
@postdatetime   char ( 20 ),
@message   ntext ,
@ip   varchar ( 15 ),
@lastedit   varchar ( 50 ),
@invisible   int ,
@usesig   int ,
@htmlon   int ,
@smileyoff   int ,
@bbcodeoff   int ,
@parseurloff   int ,
@attachment   int ,
@rate   int ,
@ratetimes   int

AS


declare   @sql   nvarchar ( 4000 )

DEClARE   @postid   int

DELETE   FROM   [ dnt_postid ]   WHERE   DATEDIFF (n, postdatetime,  GETDATE ())  > 5

INSERT   INTO   [ dnt_postid ]  ( [ postdatetime ] VALUES ( GETDATE ())

SELECT   @postid = SCOPE_IDENTITY ()

SELECT   @postid   AS  postid


INSERT   INTO   [ dnt_posts3 ] ( [ pid ] [ fid ] [ tid ] [ parentid ] [ layer ] [ poster ] ,
    
[ posterid ] [ title ] [ postdatetime ] [ message ]
    
[ ip ] [ lastedit ] [ invisible ] [ usesig ] [ htmlon ]
    
[ smileyoff ] [ bbcodeoff ] [ parseurloff ] [ attachment ] [ rate ] [ ratetimes ] )
    
VALUES ( @postid @fid @tid @parentid @layer @poster @posterid @title
    
@postdatetime @message @ip @lastedit @invisible ,
    
@usesig @htmlon @smileyoff @bbcodeoff @parseurloff @attachment
    
@rate @ratetimes )

IF   @parentid = 0
BEGIN
    
UPDATE   [ dnt_posts3 ]   SET   [ parentid ] = @postid   WHERE   [ pid ] = @postid
END

IF   @@ERROR = 0
BEGIN
    
IF    @invisible   =   0
    
BEGIN
        
UPDATE   [ dnt_statistics ]   SET   [ totalpost ] = [ totalpost ]   +   1

        
DECLARE   @fidlist   AS   VARCHAR ( 1000 )
        
DECLARE   @strsql   AS   VARCHAR ( 4000 )

        
SET   @fidlist   =   '' ;

        
SELECT   @fidlist   =   ISNULL ( [ parentidlist ] , ''
            
FROM   [ dnt_forums ]   WHERE   [ fid ]   =   @fid

        
IF   RTRIM ( @fidlist ) <> ''
        
BEGIN
            
SET   @fidlist   =   RTRIM ( @fidlist +   ' , '   +   CAST ( @fid   AS   VARCHAR ( 10 ))
        
END
        
ELSE
        
BEGIN
            
SET   @fidlist   =   CAST ( @fid   AS   VARCHAR ( 10 ))
        
END

        
--  此处为优化后的sql语句,用动态sql语句,避免进行列运算,使sql用到索引
         set   @sql   =  
' UPDATE [dnt_forums] SET
    [posts]=[posts] + 1,
    [todayposts]=CASE
    WHEN DATEDIFF(day, [lastpost], GETDATE())=0 THEN [todayposts]*1 + 1
    ELSE 1
    END,
    [lasttid]=@tid,
    [lasttitle]=@topictitle,
    [lastpost]=@postdatetime,
    [lastposter]=@poster,
    [lastposterid]=@posterid
    WHERE [fid] in (
'   +   @fidlist   +   ' ) '

        
exec  sp_executesql  @sql ,N ' @tid int,@topictitle nvarchar(60),
        @postdatetime datetime,@poster varchar(20),@posterid int
' ,
        
@tid , @topictitle , @postdatetime , @poster , @posterid


        
UPDATE   [ dnt_users ]   SET
                
[ lastpost ]   =   @postdatetime ,
                
[ lastpostid ]   =   @postid ,
                
[ lastposttitle ]   =   @title ,
                
[ posts ]   =   [ posts ]   +   1 ,
                
[ lastactivity ]   =   GETDATE ()
                
WHERE   [ uid ]   =   @posterid


        
IF   @layer <= 0
        
BEGIN
            
UPDATE   [ dnt_topics ]   SET   [ replies ] = 0 , [ lastposter ] = @poster ,
                
[ lastpost ] = @postdatetime , [ lastposterid ] = @posterid  
                
WHERE   [ tid ] = @tid
        
END
        
ELSE
        
BEGIN
            
UPDATE   [ dnt_topics ]   SET   [ replies ] = [ replies ]   +   1 , [ lastposter ] = @poster ,
                
[ lastpost ] = @postdatetime , [ lastposterid ] = @posterid  
                
WHERE   [ tid ] = @tid
        
END
    
END

    
UPDATE   [ dnt_topics ]   SET   [ lastpostid ] = @postid   WHERE   [ tid ] = @tid

    
IF   @posterid   <>   - 1
    
BEGIN
        
INSERT   [ dnt_myposts ] ( [ uid ] [ tid ] [ pid ] [ dateline ]
            
VALUES ( @posterid @tid @postid @postdatetime )
    
END

END


return   @posterid


GO

 

这里改成了sql动态语句,where后面是这样写的 WHERE [fid] in (' + @fidlist + ')',这里用到了索引,有兴趣的朋友可以自己看看执行计划,优化后的效果如何呢,看图:

优化你的DiscuzNT3.0,让它跑起来(2)发帖回帖篇_第2张图片 

 

两图对比,差距还蛮大的,ok,发帖的优化到此结束。不过discuzNT的优化还远远没有结束。

 

 

 

 

 

 

你可能感兴趣的:(discuz)