zoukankan      html  css  js  c++  java
  • 【SQLSERVER】索引的维护优化

    一、索引的利弊  

          优点: 1.大大加快数据的检索速度;

                      2.创建唯一性索引,保证数据库表中每一行数据的唯一性;

                      3.加速表和表之间的连接;

                      4.在使用分组和排序子句进行数据检索时,可以显著减少查询中分组和排序的时间。

          缺点: 1.索引需要占物理空间;

                      2.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度;

                      3.索引过多,生成执行计划的时候,也可能导致优化器需要更多的时间去选择一个合适的索引,消耗更多的CPU时间。

    二、 索引的优化方法 :

          1  索引缺失-------增加索引----新增

          2  索引过多-------合并索引----删除合并

          3  索引碎片-------索引维护----重建、重组索引

              TIP: 索引碎片会降低索引页的数据密度,在查询时造成额外的I/O。

         4   更新统计信息

    三、索引的优化思路

          通过作业调用存储过程,定期、自动地完成索引的优化维护,并记录日志;

          索引的整合,不合理索引的删除合并主要还是依赖人工判断、取舍,暂时没想到比较好的自动化处理的办法。

    四、相关的存储过程源码

          1、索引缺失,根据系统视图,自动创建可能带来最大性能提升的10条索引,并记录日志:

    建表 DBA_MissingIndexCreateRecord

    IF OBJECT_ID('DBA_MissingIndexCreateRecord') IS NOT NULL DROP TABLE DBA_MissingIndexCreateRecord
    CREATE TABLE [dbo].[DBA_MissingIndexCreateRecord](
        [Datekey] [INT] NULL,
        [PossibleImprovement] [FLOAT] NULL,
        [Last_User_Seek] [DATETIME] NULL,
        [Last_User_Scan] [DATETIME] NULL,
        [DBName] [NVARCHAR](128) NULL,
        [TableName] [NVARCHAR](128) NULL,
        [IndexName] [NVARCHAR](4000) NULL,
        [CreateSql] [NVARCHAR](4000) NULL,
        [Status] [INT] NOT NULL
    ) 
    
    GO
    
    ALTER TABLE [dbo].[DBA_MissingIndexCreateRecord] ADD  DEFAULT ((0)) FOR [Status]
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'日期' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'Datekey'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'可能的性能提高' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'PossibleImprovement'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'由可能使用了组中建议索引的用户查询所导致的上次查找日期和时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'Last_User_Seek'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'由可能使用了组中建议索引的用户查询所导致的上次扫描日期和时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'Last_User_Scan'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'数据库名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'DBName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'表名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'TableName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'IndexName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'执行脚本' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'CreateSql'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'状态 0未执行 1已执行' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord', @level2type=N'COLUMN',@level2name=N'Status'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'缺失索引创建记录表' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_MissingIndexCreateRecord'
    GO
    View Code

    过程 DBAMissingIndexCreate

    CREATE PROCEDURE [dbo].[DBAMissingIndexCreate] @Result INT = 0 OUTPUT        ----返回结果
    AS /*
    创建者:TurboWay
    申请人:TurboWay
    所属模板:数据库维护
    所属项目:数据库索引维护
    过程说明:top10缺失索引创建并记录到 DBA_DBA_MissingIndexCreateRecord
    创建时间:2017-06-27
    使用索引:
    返回字段信息:
    @Result 返回结果 0成功,-2失败
    */
        BEGIN
            SET NOCOUNT ON; 
            DECLARE @undo INT = 1;
            DECLARE @CreateSql NVARCHAR(MAX);
            DECLARE @UUID BIGINT;
            SET @UUID = CONVERT(BIGINT,(((((((YEAR(GETDATE())-1990)*12+MONTH(GETDATE()))*31+DAY(GETDATE()))*24+ 
                        CONVERT(VARCHAR(2),DATEPART(HOUR,GETDATE())))*60+CONVERT(VARCHAR(2),DATEPART(MINUTE,GETDATE())))*60+ 
                        CONVERT(VARCHAR(2),DATEPART(SECOND,GETDATE())))))*1000000+ CAST(CEILING(RAND() * 999999) AS BIGINT)
            
            BEGIN TRY
    /*记录TOP10缺失索引*/
            INSERT INTO DBA_MissingIndexCreateRecord(Datekey,PossibleImprovement,Last_User_Seek,Last_User_Scan,DBName,TableName,IndexName,CreateSql)
            SELECT a.Datekey,
                   a.PossibleImprovement,
                   a.last_user_seek,
                   a.last_user_scan,
                   a.DbName,
                   a.TableName,
                   CASE WHEN LEN(a.Index_Name) >= 128 THEN  SUBSTRING(a.Index_Name,0,CHARINDEX( '_i',a.Index_Name))+'_IncloudeTooMany'+CAST((@UUID+ROW_NUMBER()OVER(ORDER BY PossibleImprovement DESC)) AS NVARCHAR(30)) ELSE a.Index_Name END AS NewIndexName, --索引名有长度限制
                   CASE WHEN LEN(a.Index_Name) >= 128 THEN  REPLACE(CreateSql,a.Index_Name,SUBSTRING(a.Index_Name,0,CHARINDEX( '_i',a.Index_Name))+'_IncloudeTooMany'+CAST((@UUID+ROW_NUMBER()OVER(ORDER BY PossibleImprovement DESC)) AS NVARCHAR(30))) ELSE CreateSql END AS NewCreateSql
            FROM (
            SELECT  TOP 10
                    CONVERT(INT,CONVERT(NVARCHAR(8),GETDATE(),112)) AS Datekey,
                    CAST(avg_total_user_cost * avg_user_impact * ( user_scans + user_seeks )AS NUMERIC(18,2)) AS PossibleImprovement ,
                    last_user_seek ,
                    last_user_scan ,
                    DB_NAME() AS DbName ,
                    OBJECT_NAME(D.object_id) AS TableName ,
                    REPLACE(REPLACE(ISNULL('x' + REPLACE(equality_columns, ', ', '_x'), '')                 
                                    + CASE WHEN equality_columns IS NOT NULL
                                                AND inequality_columns IS NOT NULL
                                           THEN '_'
                                           ELSE ''
                                      END + ISNULL('x' + REPLACE(inequality_columns, ', ',
                                                                 '_x'), '')
                                    + CASE WHEN included_columns IS NOT NULL
                                                AND included_columns IS NOT NULL THEN '_'
                                           ELSE ''
                                      END + ISNULL('i' + REPLACE(included_columns, ', ',
                                                                 '_i'), ''), '[', ''), ']',
                            '') AS Index_Name ,
                    'CREATE INDEX [' + REPLACE(REPLACE(ISNULL('x'
                                                              + REPLACE(equality_columns,
                                                                        ', ', '_x'), '')
                                                       + CASE WHEN equality_columns IS NOT NULL
                                                                   AND inequality_columns IS NOT NULL
                                                              THEN '_'
                                                              ELSE ''
                                                         END + ISNULL('x'
                                                                      + REPLACE(inequality_columns,
                                                                          ', ', '_x'), '')
                                                       + CASE WHEN included_columns IS NOT NULL
                                                                   AND included_columns IS NOT NULL
                                                              THEN '_'
                                                              ELSE ''
                                                         END + ISNULL('i'
                                                                      + REPLACE(included_columns,
                                                                          ', ', '_i'), ''),
                                                       '[', ''), ']', '') + ']' + ' ON '
                    + [statement] + ' (' + ISNULL(equality_columns, '')
                    + CASE WHEN equality_columns IS NOT NULL
                                AND inequality_columns IS NOT NULL THEN ','
                           ELSE ''
                      END + ISNULL(inequality_columns, '') + ')' + ISNULL(' INCLUDE ('
                                                                          + included_columns
                                                                          + ')', '') AS CreateSql
            FROM    sys.dm_db_missing_index_groups AS G
                    INNER JOIN sys.dm_db_missing_index_group_stats AS GS ON G.index_group_handle = GS.group_handle
                    INNER JOIN sys.dm_db_missing_index_details AS D ON G.index_handle = D.index_handle
            WHERE   D.database_id = DB_ID()
            ORDER BY PossibleImprovement DESC  
            ) a
    
    /*创建索引,执行脚本*/
                WHILE @undo <> 0
                    BEGIN
                        SET @CreateSql = ( SELECT TOP 1
                                                    CreateSql
                                           FROM     dbo.DBA_MissingIndexCreateRecord
                                           WHERE    Status = 0
                                                    AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
                                         );
                        EXEC sp_executesql @CreateSql;
                        UPDATE  dbo.DBA_MissingIndexCreateRecord
                        SET     Status = 1
                        WHERE   Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
                                AND Status = 0
                                AND CreateSql = @CreateSql;
                        SET @undo = ( SELECT    COUNT(1)
                                      FROM      dbo.DBA_MissingIndexCreateRecord
                                      WHERE     Status = 0
                                                AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
                                    )
                    END
    
            END TRY
            BEGIN CATCH
                SET @Result = -2;
                PRINT '执行失败的语句是:''' + @CreateSql + ''' 错误信息: ' + ERROR_MESSAGE();
            END CATCH
         
            SELECT  @Result AS Result; 
    
            SET NOCOUNT OFF
        END
    View Code

          2、索引过多,将所有的索引及使用情况记录到表,再人为分析判断,删除合并:

    建表 DBA_IndexMergeRecord

    IF OBJECT_ID('DBA_IndexMergeRecord') IS NOT NULL DROP TABLE DBA_IndexMergeRecord
    CREATE TABLE [dbo].[DBA_IndexMergeRecord](
        [DbName] [NVARCHAR](255) NULL,
        [TableName] [NVARCHAR](255) NULL,
        [IndexName] [NVARCHAR](255) NULL,
        [IndexType] [NVARCHAR](60) NULL,
        [IsUnique] INT NULL,
        [Key_Cols] [NVARCHAR](MAX) NULL,
        [Included_Cols] [NVARCHAR](MAX) NULL,
        [StartDate] [DATETIME] NULL,
        [IsDrop] INT DEFAULT 0 ,
        [IsAuto] INT DEFAULT 0 ,
        [DropDate]  AS (CASE WHEN IsDrop = 1 THEN GETDATE() ELSE '1900-01-01 00:00:00.000' END ),
        [DropSql] [NVARCHAR](MAX) NULL,
        [USER_SEEKS] BIGINT NULL ,
        [USER_SCANS] BIGINT NULL ,
        [USER_LOOKUPS] BIGINT NULL ,
        [USER_UPDATES] BIGINT NULL 
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    GO
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'数据库名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'DbName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'表名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'TableName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'IndexName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引类型' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'IndexType'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'是否唯一索引' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'IsUnique'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引列' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'Key_Cols'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引包含列' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'Included_Cols'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'是否已删除 0正常 1已整合删除 2其它途径删除' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'IsDrop'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'是否自动创建 0正常 1自动创建' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'IsAuto'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'删除日期' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'DropDate'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'删除脚本' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'DropSql'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引查找的次数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'USER_SEEKS'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引扫描的次数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'USER_SCANS'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'书签查找的次数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'USER_LOOKUPS'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'增删改操作的次数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord', @level2type=N'COLUMN',@level2name=N'USER_UPDATES'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引整合记录表' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexMergeRecord'
    GO
    View Code

    过程 DBAIndexMerge

    ALTER PROCEDURE [dbo].[DBAIndexMerge] @Result INT = 0 OUTPUT        ----返回结果
    AS /*
    创建者:TurboWay
    申请人:TurboWay
    所属模板:数据库维护
    所属项目:数据库索引维护
    过程说明:将所有新增的非聚集索引记录到 DBA_IndexMergeRecord
    创建时间:2017-06-27
    使用索引:
    返回字段信息:
    @Result 返回结果 0成功,-2失败
    */
        BEGIN
            SET NOCOUNT ON; 
            BEGIN TRY
            CREATE TABLE #temp
            (tbname NVARCHAR(255) NULL ,
             idname NVARCHAR(255) NULL ,
             USER_SEEKS BIGINT ,
             USER_SCANS BIGINT,
             USER_LOOKUPS BIGINT,
             USER_UPDATES BIGINT
            )
            --索引使用情况
            INSERT INTO #temp(tbname,idname,USER_SEEKS,USER_SCANS,USER_LOOKUPS,USER_UPDATES)
            SELECT  OBJECT_NAME(S.[OBJECT_ID]) AS [OBJECT NAME] ,
                    I.[NAME] AS [INDEX NAME] ,
                    USER_SEEKS ,
                    USER_SCANS ,
                    USER_LOOKUPS ,
                    USER_UPDATES
            FROM    SYS.DM_DB_INDEX_USAGE_STATS AS S
                    INNER JOIN SYS.INDEXES AS I ON I.[OBJECT_ID] = S.[OBJECT_ID]
                                                   AND I.INDEX_ID = S.INDEX_ID
            WHERE   S.[database_id] = DB_ID()
    
            --更新索引的删除状态
            UPDATE DBA_IndexMergeRecord SET IsDrop = 2  --其它途径删除
            WHERE IsDrop = 0 AND  NOT EXISTS( SELECT 1 FROM sys.indexes a WHERE a.name= IndexName AND a.object_id = OBJECT_ID(TableName))
    
            --更新索引的重建状态
            UPDATE DBA_IndexMergeRecord SET IsDrop = 0, IsRebuild = 1  --重建
            WHERE IsDrop = 1 AND EXISTS( SELECT 1 FROM sys.indexes a WHERE a.name= IndexName AND a.object_id = OBJECT_ID(TableName))
    
            --更新索引使用情况
            UPDATE DBA_IndexMergeRecord SET USER_SEEKS = b.USER_SEEKS,
                                            USER_SCANS = b.USER_SCANS,
                                            USER_LOOKUPS = b.USER_LOOKUPS,
                                            USER_UPDATES = b.USER_UPDATES
            FROM DBA_IndexMergeRecord a, #temp b
            WHERE a.TableName = b.tbname AND a.IndexName = b.idname
    
            --记录新增索引
            INSERT INTO DBA_IndexMergeRecord([DbName],[TableName] ,[IndexName] ,[IndexType] ,[IsUnique],[Key_Cols] ,[Included_Cols] ,[StartDate] ,[DropSql],
                                             [USER_SEEKS] ,[USER_SCANS] ,[USER_LOOKUPS] ,[USER_UPDATES])
            SELECT  DB_NAME(),
                    o.name AS TableName,
                    i.name AS IndexName,
                    i.type_desc AS IndexType,
                    i.is_unique AS IsUnique,
                    SUBSTRING(ikey.cols, 3, LEN(ikey.cols)) AS Key_Cols ,
                    SUBSTRING(inc.cols, 3, LEN(inc.cols)) AS Included_Cols ,
                    STATS_DATE(o.object_id, i.index_id) AS StartDate,
                    'drop index ['+i.name+'] on ['+ o.name +']' + 
                    ' update DBA_IndexMergeRecord set IsDrop = 1 where TableName = '''+o.name+ ''' And IndexName = '''+ i.name +'''' AS DropSql,
                    USER_SEEKS ,
                    USER_SCANS ,
                    USER_LOOKUPS ,
                    USER_UPDATES
            FROM    sys.objects o
                    JOIN sys.indexes i ON i.object_id = o.object_id
                    CROSS  APPLY ( SELECT   ', ' + c.name + CASE ic.is_descending_key
                                                              WHEN 1 THEN ' DESC'
                                                              ELSE ''
                                                            END
                                   FROM     sys.index_columns ic
                                            JOIN sys.columns c ON ic.object_id = c.object_id
                                                                  AND ic.column_id = c.column_id
                                   WHERE    ic.object_id = i.object_id
                                            AND ic.index_id = i.index_id
                                            AND ic.is_included_column = 0
                                   ORDER BY ic.key_ordinal
                                 FOR
                                   XML PATH('')
                                 ) AS ikey ( cols )
                    OUTER  APPLY ( SELECT   ', ' + c.name
                                   FROM     sys.index_columns ic
                                            JOIN sys.columns c ON ic.object_id = c.object_id
                                                                  AND ic.column_id = c.column_id
                                   WHERE    ic.object_id = i.object_id
                                            AND ic.index_id = i.index_id
                                            AND ic.is_included_column = 1
                                   ORDER BY ic.index_column_id
                                 FOR
                                   XML PATH('')
                                 ) AS inc ( cols )
                    LEFT JOIN (SELECT * 
                               FROM SYS.DM_DB_INDEX_USAGE_STATS 
                               WHERE [database_id] = DB_ID()) t ON t.index_id = i.index_id AND t.object_id = i.object_id
            WHERE o.type = 'u' 
              AND i.type IN (1,2)
              AND NOT EXISTS(SELECT 1 FROM DBA_IndexMergeRecord rd WHERE o.name = rd.TableName AND i.name = rd.IndexName)
            ORDER BY o.name ,
                    i.index_id 
            
            --更新索引来源
            UPDATE DBA_IndexMergeRecord SET IsAuto = 1  --自动创建
              FROM DBA_IndexMergeRecord a ,DBA_MissingIndexCreateRecord b
             WHERE IsAuto = 0 AND a.TableName = b.TableName AND a.IndexName= b.IndexName 
             
            END TRY
            BEGIN CATCH
                SET @Result = -2;
            END CATCH
         
            SELECT  @Result AS Result; 
    
            SET NOCOUNT OFF
        END
    
    
    GO
    View Code

          3、索引碎片,通过索引重组或重建,减少或消除索引,并记录日志:

    建表 DBA_IndexDefragRecord

    CREATE TABLE [dbo].[DBA_IndexDefragRecord](
        [Datekey] [INT] NULL,
        [DBName] [NVARCHAR](255) NULL,
        [SchemaName] [NVARCHAR](255) NULL,
        [TableName] [NVARCHAR](255) NULL,
        [IndexName] [NVARCHAR](255) NULL,
        [AvgFragmentB] [DECIMAL](5, 2) NULL,
        [AvgFragmentE] [DECIMAL](5, 2) NULL,
        [DefragSql] [NVARCHAR](MAX) NULL,
        [Status] [INT] NULL
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    GO
    
    ALTER TABLE [dbo].[DBA_IndexDefragRecord] ADD  DEFAULT ((0)) FOR [Status]
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'日期' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'Datekey'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'数据库名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'DBName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'架构' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'SchemaName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'表名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'TableName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'IndexName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'维护前的碎片程度' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'AvgFragmentB'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'维护后的碎片程度' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'AvgFragmentE'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'执行脚本' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'DefragSql'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'状态 0未执行 1已执行' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord', @level2type=N'COLUMN',@level2name=N'Status'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'索引维护记录表' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_IndexDefragRecord'
    GO
    View Code

    过程 DBA_IndexDefrag

    CREATE PROCEDURE [dbo].[DBAIndexDefrag] @Result INT = 0 OUTPUT        ----返回结果
    AS /*
    创建者:TurboWay
    申请人:TurboWay
    所属模板:数据库维护
    所属项目:数据库索引维护
    过程说明:整理索引碎片并记录到 DBA_IndexDefragRecord
    创建时间:2017-06-27
    使用索引:
    返回字段信息:
    @Result 返回结果 0成功,-2失败
    */
        BEGIN
            SET NOCOUNT ON; 
            DECLARE @undo INT = 1;
            DECLARE @DefragSql NVARCHAR(MAX);
            IF OBJECT_ID('tempdb..#Frag') IS NOT NULL
                DROP TABLE #Frag;
            CREATE TABLE #Frag
                (
                  Datekey INT ,
                  DBName NVARCHAR(255) ,
                  SchemaName NVARCHAR(255) ,
                  TableName NVARCHAR(255) ,
                  IndexName NVARCHAR(255) ,
                  AvgFragmentE DECIMAL(5, 2)
                );
    
            BEGIN TRY
    /*记录索引碎片情况和维护脚本,索引碎片10-30,重组索引;索引碎片30以上,重建索引*/
                IF NOT EXISTS ( SELECT  1
                                FROM    DBA_IndexDefragRecord
                                WHERE   Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112)) )
                    BEGIN
                        INSERT  INTO DBA_IndexDefragRecord
                                ( Datekey ,
                                  DBName ,
                                  SchemaName ,
                                  TableName ,
                                  IndexName ,
                                  AvgFragmentB ,
                                  DefragSql
                                )
                                SELECT  CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112)) Datekey ,
                                        DB_NAME() AS DBName ,
                                        sysSc.name AS SchemaName ,
                                        sysTa.name AS TableName ,
                                        sysIn.name AS IndexName ,
                                        func.avg_fragmentation_in_percent AvgFragmentB ,
                                        CASE WHEN func.avg_fragmentation_in_percent BETWEEN 10.0
                                                                  AND
                                                                  30.0
                                             THEN N'ALTER INDEX [' + sysIn.name
                                                  + '] ON [' + DB_NAME() + '].['
                                                  + sysSc.name + '].['
                                                  + sysTa.name + '] REORGANIZE'
                                             WHEN func.avg_fragmentation_in_percent > 30.0
                                             THEN N'ALTER INDEX [' + sysIn.name
                                                  + '] ON [' + DB_NAME() + '].['
                                                  + sysSc.name + '].['
                                                  + sysTa.name
                                                  + '] REBUILD'
                                        END AS DefraSql
                                FROM    sys.dm_db_index_physical_stats(DB_ID(),
                                                                  NULL, NULL, NULL,
                                                                  NULL) AS func
                                        INNER JOIN sys.indexes AS sysIn ON func.object_id = sysIn.object_id
                                                                  AND func.index_id = sysIn.index_id
                                        LEFT JOIN sys.all_objects AS sysTa ON func.object_id = sysTa.object_id
                                        LEFT JOIN sys.schemas AS sysSc ON sysTa.schema_id = sysSc.schema_id
                                WHERE   sysIn.index_id > 0
                                        AND func.page_count > 8                        --只维护数据页有8页以上的索引
                                        AND func.avg_fragmentation_in_percent > 10     --只维护碎片达到10%以上的索引
                                        AND sysTa.type IN ( 'u', 'v' )                 --维护表索引、视图索引
                                ORDER BY func.avg_fragmentation_in_percent;
                    END;
    
    /*整理索引碎片,执行脚本*/
                WHILE @undo <> 0
                    BEGIN
                        SET @DefragSql = ( SELECT TOP 1
                                                    DefragSql
                                           FROM     dbo.DBA_IndexDefragRecord
                                           WHERE    Status = 0
                                                    AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
                                         );
                        EXEC sp_executesql @DefragSql;
                        UPDATE  dbo.DBA_IndexDefragRecord
                        SET     Status = 1
                        WHERE   Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
                                AND Status = 0
                                AND DefragSql = @DefragSql;
                        SET @undo = ( SELECT    COUNT(1)
                                      FROM      dbo.DBA_IndexDefragRecord
                                      WHERE     Status = 0
                                                AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
                                    );
                    END;
    
    /*更新维护后的索引碎片程度*/
                INSERT  INTO #Frag
                        ( Datekey ,
                          DBName ,
                          SchemaName ,
                          TableName ,
                          IndexName ,
                          AvgFragmentE
                        )
                        SELECT  CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112)) Datekey ,
                                DB_NAME() AS DBName ,
                                sysSc.name AS SchemaName ,
                                sysTa.name AS TableName ,
                                sysIn.name AS IndexName ,
                                func.avg_fragmentation_in_percent AvgFragmentE
                        FROM    sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL,
                                                               NULL, NULL) AS func
                                INNER JOIN sys.indexes AS sysIn ON func.object_id = sysIn.object_id
                                                                  AND func.index_id = sysIn.index_id
                                LEFT JOIN sys.all_objects AS sysTa ON func.object_id = sysTa.object_id
                                LEFT JOIN sys.schemas AS sysSc ON sysTa.schema_id = sysSc.schema_id
                        WHERE   sysIn.index_id > 0
                                AND sysTa.type IN ( 'u', 'v' );                 --维护表索引、视图索引
    
                UPDATE  dbo.DBA_IndexDefragRecord
                SET     AvgFragmentE = b.AvgFragmentE
                FROM    dbo.DBA_IndexDefragRecord a ,
                        #Frag b
                WHERE   a.Datekey = b.Datekey
                        AND a.DBName = b.DBName
                        AND a.SchemaName = b.SchemaName
                        AND a.TableName = b.TableName
                        AND a.IndexName = b.IndexName;
    
            END TRY
            BEGIN CATCH
                SET @Result = -2;
                PRINT '执行失败的语句是:''' + @DefragSql + ''' 错误信息: ' + ERROR_MESSAGE();
            END CATCH;
         
            SELECT  @Result AS Result; 
    
            DROP TABLE #Frag;
            SET NOCOUNT OFF;
        END;
    View Code

          4、更新统计信息,并记录日志(这个日志并没什么用,但统计信息是有意义的,有利于优化器选择合理的索引、生成最优的执行计划)

    建表 DBA_StatisticsUpdateRecord

    CREATE TABLE [dbo].[DBA_StatisticsUpdateRecord](
        [Datekey] [INT] NULL,
        [DBName] [NVARCHAR](255) NULL,
        [SchemaName] [NVARCHAR](255) NULL,
        [TableName] [NVARCHAR](255) NULL,
        [StatisticsName] [NVARCHAR](255) NULL,
        [LastStatUpdateDate] [DATETIME] NULL,
        [RowModCTR] [INT] NULL,
        [TotalRowsInTable] [INT] NULL,
        [UpdateSql] [NVARCHAR](MAX) NULL,
        [Status] [INT] NULL
    ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
    
    GO
    
    ALTER TABLE [dbo].[DBA_StatisticsUpdateRecord] ADD  DEFAULT ((0)) FOR [Status]
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'日期' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'Datekey'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'数据库名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'DBName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'架构' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'SchemaName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'表名' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'TableName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'统计信息名称' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'StatisticsName'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'最后更新的时间' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'LastStatUpdateDate'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'抽样行数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'RowModCTR'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'实际行数' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'TotalRowsInTable'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'执行脚本' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'UpdateSql'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'状态 0未执行 1已执行' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord', @level2type=N'COLUMN',@level2name=N'Status'
    GO
    
    EXEC sys.sp_addextendedproperty @name=N'MS_Description', @value=N'统计信息更新记录表' , @level0type=N'SCHEMA',@level0name=N'dbo', @level1type=N'TABLE',@level1name=N'DBA_StatisticsUpdateRecord'
    GO
    View Code

    过程 DBAStatisticsUpdate

    CREATE PROCEDURE [dbo].[DBAStatisticsUpdate] @Result INT = 0 OUTPUT        ----返回结果
    AS 
    /*
    创建者:TurboWay
    申请人:TurboWay
    所属模板:数据库维护
    所属项目:数据库统计信息维护
    过程说明:更新过时的统计信息并记录到 DBA_StatisticsUpdateRecord
    创建时间:2017-06-27
    使用索引:
    返回字段信息:
    @Result 返回结果 0成功,-2失败
    */
        BEGIN
            SET NOCOUNT ON; 
            DECLARE @undo INT = 1;
            DECLARE @UpdateSql NVARCHAR(MAX);
    
            BEGIN TRY
    /*记录过时的统计信息*/
                INSERT  INTO DBA_StatisticsUpdateRecord
                        ( [Datekey] ,
                          [DBName] ,
                          [SchemaName] ,
                          [TableName] ,
                          [StatisticsName] ,
                          [LastStatUpdateDate] ,
                          [RowModCTR] ,
                          [TotalRowsInTable] ,
                          [UpdateSql]
                        )
                        SELECT DISTINCT
                                CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112)) Datekey ,
                                DB_NAME() DBName ,
                                SCHEMA_NAME(SO.schema_id) SchemaName ,
                                OBJECT_NAME(SI.object_id) AS TableName ,
                                SI.name AS StatisticsName ,
                                STATS_DATE(SI.object_id, SI.index_id) AS LastStatUpdateDate ,
                                SSI.rowmodctr AS RowModCTR ,
                                SP.rows AS TotalRowsInTable ,
                                'UPDATE STATISTICS [' + DB_NAME() + '].['
                                + SCHEMA_NAME(SO.schema_id) + '].['
                                + OBJECT_NAME(SI.object_id) + ']' + SPACE(2)
                                +'[' +SI.name+']' AS UpdateSql
                        FROM    sys.indexes AS SI ( NOLOCK )
                                INNER JOIN sys.objects AS SO ( NOLOCK ) ON SI.object_id = SO.object_id
                                INNER JOIN sys.sysindexes SSI ( NOLOCK ) ON SI.object_id = SSI.id
                                                                  AND SI.index_id = SSI.indid
                                INNER JOIN sys.partitions AS SP ON SI.object_id = SP.object_id
                        WHERE   SSI.rowmodctr > 0
                                AND STATS_DATE(SI.object_id, SI.index_id) IS NOT NULL
                                AND SO.type = 'U'
                        ORDER BY RowModCTR DESC;
    
    /*更新过时的统计信息,执行脚本*/
                WHILE @undo <> 0
                    BEGIN
                        SET @UpdateSql = ( SELECT TOP 1
                                                    UpdateSql
                                           FROM     dbo.DBA_StatisticsUpdateRecord
                                           WHERE    Status = 0
                                                    AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
                                         );
                        EXEC sp_executesql @UpdateSql;
                        UPDATE  dbo.DBA_StatisticsUpdateRecord
                        SET     Status = 1
                        WHERE   Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
                                AND Status = 0
                                AND UpdateSql = @UpdateSql;
                        SET @undo = ( SELECT    COUNT(1)
                                      FROM      dbo.DBA_StatisticsUpdateRecord
                                      WHERE     Status = 0
                                                AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))
                                    );
                    END;
            END TRY
            BEGIN CATCH
                SET @Result = -2;
                PRINT '执行失败的语句是:' + @UpdateSql;
            END CATCH;
         
            SELECT  @Result AS Result; 
    
            SET NOCOUNT OFF;
        END;
    View Code

    五、简化作业调用

          用一个过程,灵活调用以上的过程。

          再用作业来调用这个过程DBAMaintenance,定期执行,达到自动维护的效果。

    过程DBAMaintenance

    ALTER PROCEDURE [dbo].[DBAMaintenance] 
    @Returnvalue INT = 0 OUTPUT        ----返回结果
    AS 
    /*
    创建者:TurboWay
    申请人:TurboWay
    所属模板:数据库维护
    所属项目:数据库维护
    过程说明:执行数据库维护的过程,供作业直接调用
    创建时间:2017-06-27
    使用索引:
    返回字段信息:
    Returnvalue 返回结果 0 没有执行,成功
                     大于0 表示执行次数
                        -2 执行失败,超出执行次数
    */ 
    BEGIN
        /*整理索引碎片*/
        DECLARE @IsSucess INT =0 
        DECLARE @Count INT =0 --执行计数
    
        EXEC DBAIndexDefrag @IsSucess OUTPUT
    
        IF @IsSucess = -2 --由于重建索引可能被高并发的查询,阻塞牺牲,导致失败,所以需要等待重复执行
           BEGIN
                 WHILE EXISTS(SELECT 1 FROM dbo.DBA_IndexDefragRecord WHERE Status = 0 AND Datekey = CONVERT(INT, CONVERT(NVARCHAR(8), GETDATE(), 112))) --存在未执行的脚本
                      AND @Count < 30 --执行过程的次数不得高于30,防止出现死循环
                        BEGIN
                            WAITFOR DELAY '00:00:10' --延迟10秒执行,等待引起阻塞的查询完成
                            BEGIN
                                EXEC DBAIndexDefrag  --再次整理碎片
                                SET @Count = @Count + 1
                            END
                        END
           END
        /*更新统计信息*/
        EXEC DBAStatisticsUpdate  
    
        /*创建缺失索引top10*/
        EXEC DBAMissingIndexCreate
    
        /*记录索引整体情况*/
        EXEC DBAIndexMerge
    
        /*返回执行情况*/
        SELECT @Returnvalue = CASE WHEN @Count = 0 THEN 0 
                                   WHEN @Count > 0 AND @Count < 30 THEN @Count
                                   ELSE -2 
                                   END 
        SELECT @Returnvalue AS Returnvalue
    END
    
    
    
    GO
    View Code

     

  • 相关阅读:
    word设置的密码忘了怎么办?
    Navicat Report Viewer 设置 HTTP 的方法
    如何处理Navicat Report Viewer 报表
    excel密码忘记了怎么办
    Beyond Compare文本比较搜索功能详解
    Popular Cows POJ
    Problem B. Harvest of Apples HDU
    网络流模型整理
    The Shortest Statement CodeForces
    Vasya and Multisets CodeForces
  • 原文地址:https://www.cnblogs.com/TurboWay/p/7061022.html
Copyright © 2011-2022 走看看