zoukankan      html  css  js  c++  java
  • 优化你的DiscuzNT3.0,让它跑起来(3)删帖篇

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

    在前两篇已经对看帖,发帖回帖做了优化,做过优化之后论坛访问起来果然顺畅多了。随着论坛帖子数的不断增加,论坛出现了一个新的问题,管理员在删帖的时候经常报错,超时,删除失败。

    由于有了之前的两次经验,现在找起问题来比之前要快多了,现在先找到删帖的存储过程 dnt_deletetopicbytidlist3,脚本如下:

    ALTER   PROCEDURE [dnt_deletetopicbytidlist3]
    @tidlist AS VARCHAR(2000),
    @chanageposts AS BIT
    AS

        
    DECLARE @postcount int
        
    DECLARE @topiccount int
        
    DECLARE @todaycount int
        
    DECLARE @sqlstr nvarchar(4000)
        
    DECLARE @fid varchar(2000)
        
    DECLARE @posterid varchar(200)
        
    DECLARE @tempFid int
        
    DECLARE @tempPosterid int
        
    DECLARE @tempLayer int
        
    DECLARE @temppostdatetime datetime

        
    DECLARE @tempfidlist AS VARCHAR(1000)    
        
        
    SET @fid = ''
        
    SET @posterid = ''
        
    SET @postcount=0
        
    SET @topiccount=0
        
    SET @todaycount=0

        
    SET @tempfidlist = '';


        
    IF @tidlist<>''
        
    BEGIN
            
    -- 问题:列运算
            DECLARE cu_dnt_posts CURSOR FOR SELECT [fid],[posterid],[layer],[postdatetime] 
                
    FROM [dnt_posts3] 
                
    WHERE CHARINDEX(','+RTRIM([dnt_posts3].[tid])+','','+@tidlist+','> 0
            
            
    OPEN cu_dnt_posts
            
    FETCH NEXT FROM cu_dnt_posts into @tempFid,@tempPosterid,@tempLayer,@temppostdatetime
            
    WHILE @@FETCH_STATUS = 0
            
    BEGIN
                
    SET @postcount = @postcount + 1

                
    IF @tempLayer = 0
                
    BEGIN
                    
    SET @topiccount = @topiccount + 1
                            
                
    END

                
    IF DATEDIFF(d,@temppostdatetime,GETDATE()) = 0
                
    BEGIN
                    
    SET @todaycount = @todaycount + 1
                
    END

                
    IF CHARINDEX(',' + LTRIM(STR(@tempFid)) + ',',@fid + ','= 0
                
    BEGIN
                    
    -- 把所有关联的论坛id保存到变量 @tempfidlist,后面会用到
                    SELECT @tempfidlist = ISNULL([parentidlist],''
                        
    FROM [dnt_forums] WHERE [fid] = @tempFid

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

                
    END

                
    IF @chanageposts = 1
                
    BEGIN
                    
    -- 更新用户发帖数量
                    UPDATE [dnt_users] SET [posts] = [posts] - 1 WHERE [uid] = @tempPosterid
                
    END
                
                
    FETCH NEXT FROM cu_dnt_posts into @tempFid,@tempPosterid,@tempLayer,@temppostdatetime

            
    END

            
    CLOSE cu_dnt_posts
            
    DEALLOCATE cu_dnt_posts


            
    IF LEN(@fid)>0
            
    BEGIN
                
    SET @fid = SUBSTRING(@fid,2,LEN(@fid)-1)
        
            
    IF @chanageposts = 1
            
    BEGIN
        
                
    UPDATE [dnt_statistics] SET [totaltopic]=[totaltopic] - @topiccount[totalpost]=[totalpost] - @postcount

                
    -- 问题:列运算
                UPDATE [dnt_forums] 
                    
    SET [posts]=[posts] - @postcount,
                    
    [topics]=[topics] - @topiccount
                    
    [todayposts] = [todayposts] - @todaycount  
                    
    WHERE CHARINDEX(','+RTRIM([fid])+','','+@fid+','> 0

            
    END
        
            
    -- 问题:列运算
            DELETE FROM [dnt_favorites] WHERE CHARINDEX(','+RTRIM([tid])+','','+@tidlist+','> 0 AND [typeid]=0

            
    -- 问题:列运算
            DELETE FROM [dnt_polls] WHERE CHARINDEX(','+RTRIM([tid])+','','+@tidlist+','> 0

            
    -- 问题:列运算
            DELETE FROM [dnt_posts3] WHERE CHARINDEX(','+RTRIM([tid])+','','+@tidlist+','> 0

            
    -- 问题:列运算
            DELETE FROM [dnt_mytopics] WHERE CHARINDEX(','+RTRIM([tid])+','','+@tidlist+','> 0
        
        
    END
        
        
    -- 问题:列运算
        DELETE FROM [dnt_topics] WHERE CHARINDEX((','+RTRIM([closed])+','), ','+@tidlist+','> 0 
            
    OR CHARINDEX(','+RTRIM([tid])+','','+@tidlist+','> 0
            
        
    -- 问题:列运算
        UPDATE [dnt_tags] SET [count]=[count]-1[fcount]=[fcount]-1 
            
    WHERE [tagid] IN (SELECT [tagid] FROM [dnt_topictags] 
                
    WHERE CHARINDEX(','+RTRIM([tid])+','','+@tidlist+','> 0 ) 
            
        
    -- 问题:列运算
        DELETE FROM [dnt_topictags] WHERE CHARINDEX(','+RTRIM([tid])+','','+@tidlist+','> 0
            
        
    -- 问题:列运算
        DELETE FROM [dnt_topictagcaches] WHERE CHARINDEX(','+RTRIM([tid])+','','+@tidlist+','> 0 
            
    OR CHARINDEX((','+RTRIM([linktid])+','), ','+@tidlist+','> 0


    end

    go 

    删帖的操作流程是这样的:

    1)传入@tidlist, 用逗号把帖子id隔开,比如“1,2,3,4,5” 这样的格式;

    2)用游标 cu_dnt_posts 循环读取 dnt_posts3 表(此处用到了列运算,已标出);

    3)找到该贴子id关联的所有论坛id,放到@tempfidlist变量,后面会用到这个变量,这个语句

            SELECT @tempfidlist = ISNULL([parentidlist],'') FROM [dnt_forums] WHERE [fid] = @tempFid 

            同时更新用户的发帖数量,这一句 UPDATE [dnt_users] SET [posts] = [posts] - 1 WHERE [uid] = @tempPosterid 

    4)关闭游标

    5)更新统计信息,这句 UPDATE [dnt_statistics] SET [totaltopic]=[totaltopic] - @topiccount, [totalpost]=[totalpost] - @postcount

    6)更新论坛发帖数量,这句  UPDATE [dnt_forums] 

                    SET [posts]=[posts] - @postcount,
                    [topics]=[topics] - @topiccount, 
                    [todayposts] = [todayposts] - @todaycount  
                    WHERE CHARINDEX(','+RTRIM([fid])+',', ','+@fid+',') > 0 

    7)删除收藏夹,删除回复表,删除用户发帖信息表 等等

    8)删除主贴表,删除标签表 等等


    这个过程代码量很大,逻辑稍微的有些复杂, 更糟糕的是里面大半的操作都做了列运算,charindex()这个函数就是,oh,my god,我们论坛目前的主贴表是 320万,其他回复表,最少的也有400万,最多有1000万,我可以分负责任的说,只要一运行这个过程,就死定了。不信?我们还是请profiler来帮我们看看吧(当然这是在测试环境,生产环境请勿尝试,否则后果自负)。

    后台管理删帖界面: 

     

    点击提交,等了N久,得到下图(超时了):

     

       看看profiler跟踪的结果,如下图:

     

    看看这几个性能参数 cpu 27360,reads 71290,duration 30016 ,看到这里应该了然了,不超时是不可能的。

    优化的方案有很多种,我这里只提供一种, 修改他的存储过程,不修改他的业务逻辑,我的原理是,用in代替charindex()函数,到目前为止dnt所有的数据库性能问题都和charindex()这个函数有关,而我也都是改成in,这次比之前稍微有难度,因为他涉及到了游标,修改要谨慎,改过之后的过程脚本如下:

    ALTER      PROCEDURE [dnt_deletetopicbytidlist3]
    @tidlist AS VARCHAR(2000),
    @chanageposts AS BIT
    AS

    DECLARE @postcount int
    DECLARE @topiccount int
    DECLARE @todaycount int
    DECLARE @sqlstr nvarchar(4000)
    DECLARE @fid varchar(2000)
    DECLARE @posterid varchar(200)

    declare @sql nvarchar(4000)

    SET @fid = ''
    SET @posterid = ''
    SET @postcount=0
    SET @topiccount=0
    SET @todaycount=0


    IF @tidlist = ''
        
    return

    set @sql  = 
    'DECLARE @tempFid int ' + char(13+ char(10+ 
    'DECLARE @tempPosterid int ' + char(13+ char(10+ 
    'DECLARE @tempLayer int ' + char(13+ char(10+ 
    'DECLARE @temppostdatetime datetime ' + char(13+ char(10+ 
    'DECLARE @tempfidlist AS VARCHAR(1000) ' + char(13+ char(10+ 
    'SET @tempfidlist = '''' '  + char(13+ char(10+ 
    'declare @cu_dnt_posts cursor ' + char(13+ char(10+ 
    'set @cu_dnt_posts = CURSOR read_only forward_only FOR 
        SELECT [fid],[posterid],[layer],[postdatetime] FROM [dnt_posts3] WHERE tid in(
    ' 
        
    + @tidlist + '' +char(13+ char(10+ 
    'OPEN @cu_dnt_posts ' +char(13+ char(10+ 
    'FETCH NEXT FROM @cu_dnt_posts into @tempFid,@tempPosterid,@tempLayer,@temppostdatetime ' 
        
    + char(13+ char(10+ 
    'WHILE @@FETCH_STATUS = 0 ' +char(13+ char(10+ 
    'BEGIN ' +char(13+ char(10+ 

    '   SET @postcount = @postcount + 1 ' +char(13+ char(10+ 

    '   IF @tempLayer = 0' +char(13+ char(10+ 
    '       SET @topiccount = @topiccount + 1 ' +char(13+ char(10+ 

    '   IF DATEDIFF(d,@temppostdatetime,GETDATE()) = 0 ' + char(13+ char(10+ 
    '       SET @todaycount = @todaycount + 1 ' +char(13+ char(10+ 

    '   IF CHARINDEX('','' + LTRIM(STR(@tempFid)) + '','',@fid + '','') = 0 ' 
        
    + char(13+ char(10+ 
    '   BEGIN ' + char(13+ char(10+ 
    '       SELECT @tempfidlist = ISNULL([parentidlist],'''') FROM [dnt_forums] WHERE [fid] = @tempFid ' 
        
    + char(13+ char(10+ 
    '       IF RTRIM(@tempfidlist)<>'''' ' + char(13+ char(10+ 
    '           SET @fid = RTRIM(@fid) + '','' +  RTRIM(@tempfidlist) + '','' + CAST(@tempFid AS VARCHAR(10)) ' 
        
    + char(13+ char(10+ 
    '       ELSE ' +char(13+ char(10+ 
    '           SET @fid =RTRIM(@fid) + '','' +  CAST(@tempFid AS VARCHAR(10)) ' + char(13+ char(10+ 
    '   END ' +char(13+ char(10+ 

    '    IF @chanageposts = 1 ' +char(13+ char(10+ 
    '        UPDATE [dnt_users] SET [posts] = [posts] - 1 WHERE [uid] = @tempPosterid '  
        
    + char(13+ char(10+ 

    '   FETCH NEXT FROM @cu_dnt_posts into @tempFid,@tempPosterid,@tempLayer,@temppostdatetime ' 
        
    + char(13+ char(10+ 

    'END ' +char(13+ char(10+ 
    'CLOSE @cu_dnt_posts ' + char(13+ char(10+ 
    'DEALLOCATE @cu_dnt_posts '


    exec sp_executesql @sql,N'@postcount int output,@topiccount int output,
        @todaycount int output,@fid varchar(2000) output,@chanageposts int
    ',
        
    @postcount output,@topiccount output,@todaycount output,@fid output,@chanageposts


    IF LEN(@fid)>0
    BEGIN    
        
        
         
    SET @fid = SUBSTRING(@fid,2,LEN(@fid)-1)
        
         
    IF @chanageposts = 1
             
    BEGIN
        
                 
    UPDATE [dnt_statistics] SET [totaltopic]=[totaltopic] - @topiccount[totalpost]=[totalpost] - @postcount
                 
                 
    set @sql = 
                 
    'UPDATE [dnt_forums] 
                 SET [posts]=[posts] - @postcount,  
                 [topics]=[topics] - @topiccount, 
                 [todayposts] = [todayposts] - @todaycount  
                 where fid in (
    ' + @fid +  ')'                            
                 
    exec sp_executesql @sql,N'@postcount int,@topiccount int,@todaycount int',
                    
    @postcount,@topiccount,@todaycount
                 
             
    END
        
         
    set @sql = 'DELETE FROM [dnt_favorites] WHERE tid in (' + @tidlist + ') AND [typeid]=0'
         
    exec sp_executesql @sql
         
    print @sql
        
         
    set @sql = 'DELETE FROM [dnt_polls] WHERE tid in (' + @tidlist + ')'
         
    exec sp_executesql @sql
         
    print @sql


         
    set @sql = 'DELETE FROM [dnt_posts3] WHERE tid in (' + @tidlist + ')'
         
    exec sp_executesql @sql
         
    print @sql
        
         
    set @sql = 'DELETE FROM [dnt_mytopics] WHERE tid in (' + @tidlist + ')'
         
    exec sp_executesql @sql
         
    print @sql
         
    END
            
    set @sql = 'DELETE FROM [dnt_topics] WHERE [closed] in (' + @tidlist + ') or [tid] in (' + @tidlist + ')'
    exec sp_executesql @sql
    print @sql

    set @sql = 'UPDATE [dnt_tags] SET [count]=[count]-1, [fcount]=[fcount]-1 WHERE [tagid] IN (SELECT [tagid] FROM [dnt_topictags] WHERE tid in (' + @tidlist + ') ) '
    exec sp_executesql @sql
    print @sql

    set @sql = 'DELETE FROM [dnt_topictags] WHERE tid in (' + @tidlist + ')'
    exec sp_executesql @sql
    print @sql

    set @sql = 'DELETE FROM [dnt_topictagcaches] WHERE tid in (' + @tidlist + ') OR linktid in (' + @tidlist + ')'
    exec sp_executesql @sql

    GO 

    感兴趣的童鞋可以把@sql打印出来,看看sql脚本,修改时需谨慎,优化后的过程如何呢,我们来看看profiler的效果:

     

    看性能参数:cpu 62,reads 2684,duration 420;KO。

    如果有童鞋正在使用dnt3.0,并且你希望你的论坛真正的跑起来,不妨试一试。

    到目前为止,本系列已经出了3篇随笔,都是针对dnt的数据库性能问题提供了一些基本的思路和解决方案,以后数据库不再是重点;下一篇会针对dnt的asp.net端,如有兴趣敬请期待。

    下篇预告:优化你的DiscuzNT,让它跑起来(4)asp.net 缓存和死锁 


  • 相关阅读:
    mac 端口转发方案
    Js 函数
    for 循环语句 与 while 循环
    Javascript 基础2
    Javascript 基础 1
    设计模式学习(一)-概述
    java基础知识-对象和类
    java基础知识-(数组)
    Java面试-框架篇(SSM-SpringMVC)
    Java面试-框架篇(SSM-Mybatis)
  • 原文地址:https://www.cnblogs.com/gezifeiyang/p/2073575.html
Copyright © 2011-2022 走看看