T-SQL Recipes之Index Defragmentation

The Problem

索引一直是优化查询性能的不二法门。其中一个最直接的问题便是当审查一个低性能查询语句时,检查索引是否在正确的地方或者加索引没有。运行一个batchjob查看索引碎片,必要时采取步骤优化索引碎片是日常维护程序中不可缺少的。

今天的主题便是如何判定数据库中的索引碎片和优化措施

我们经常会用到sys.dm_db_index_physical_stats表来查看索引信息

示例:

USE AdventureWorks2014
GO

DECLARE @database_name VARCHAR(100) = ‘AdventureWorks2014‘;
SELECT  SD.name AS database_name ,
        SO.name AS object_name ,
        SI.name AS index_name ,
        IPS.index_type_desc ,
        IPS.page_count ,
        IPS.avg_fragmentation_in_percent
FROM    sys.dm_db_index_physical_stats(NULL, NULL, NULL, NULL, NULL) IPS
        INNER JOIN sys.databases SD ON SD.database_id = IPS.database_id
        INNER JOIN sys.indexes SI ON SI.index_id = IPS.index_id
        INNER JOIN sys.objects SO ON SO.object_id = SI.object_id
                                     AND IPS.object_id = SO.object_id
WHERE   alloc_unit_type_desc = ‘IN_ROW_DATA‘
        AND index_level = 0
        AND SD.name = @database_name
ORDER BY IPS.avg_fragmentation_in_percent DESC;

当知道索引碎片信息以后,我们应该如何优化呢?或者在任何数据库中查看索引碎片和优化呢?

The Solution

优化索引有两种方案

  • Index Rebuild:

    When an index is rebuilt , it is completely replaced with a new copy of the index, built from scratch as though
    it were just newly created. In SQL Server Standard edition, this is an offline operation, meaning that it can
    cause contention while running.

  • Index Reorganization: 

    Reorganizing an index results in cleanup at the leaf level, reordering pages and reapplying the fill factor as
    necessary. This operation is always online, regardless of the edition of SQL Server you are running and can
    be interrupted at any time with no ill effects.

现在我们通过百分比来判定索引是rebulid还是reorganization

IF OBJECT_ID(‘dbo.index_maintenance_demo‘,‘P‘) IS NOT NULL
BEGIN
    DROP PROCEDURE dbo.index_maintenance_demo;
END

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE dbo.index_maintenance_demo
    @reorganization_percentage TINYINT = 10 ,
    @rebuild_percentage TINYINT = 35 ,
    @print_results_only BIT = 1
AS
    BEGIN
        DECLARE @sql_command NVARCHAR(MAX) = ‘‘;
        DECLARE @parameter_list NVARCHAR(MAX) = ‘@reorganization_percentage TINYINT, @rebuild_percentage TINYINT‘
        DECLARE @database_name NVARCHAR(MAX);
        DECLARE @database_list TABLE
            (
              database_name NVARCHAR(MAX) NOT NULL
            );
        INSERT  INTO @database_list
                ( database_name
                )
                SELECT  name
                FROM    sys.databases
                WHERE   databases.name NOT IN ( ‘msdb‘, ‘master‘, ‘TempDB‘,
                                                ‘model‘,‘ReportServer$SQL2014‘ );
        CREATE TABLE #index_maintenance
            (
              database_name NVARCHAR(MAX) ,
              schema_name NVARCHAR(MAX) ,
              object_name NVARCHAR(MAX) ,
              index_name NVARCHAR(MAX) ,
              index_type_desc NVARCHAR(MAX) ,
              page_count BIGINT ,
              avg_fragmentation_in_percent FLOAT ,
              index_operation NVARCHAR(MAX)
            );

        SELECT  @sql_command = @sql_command + ‘
USE [‘ + database_name + ‘]

INSERT  INTO #index_maintenance
        ( database_name ,
          schema_name ,
          object_name ,
          index_name ,
          index_type_desc ,
          page_count ,
          avg_fragmentation_in_percent ,
          index_operation
        )
        SELECT  CAST(SD.name AS NVARCHAR(MAX)) AS database_name ,
                CAST(SS.name AS NVARCHAR(MAX)) AS schema_name ,
                CAST(SO.name AS NVARCHAR(MAX)) AS object_name ,
                CAST(SI.name AS NVARCHAR(MAX)) AS index_name ,
                IPS.index_type_desc ,
                IPS.page_count ,
                -- Be sure to filter as much as possible...this can return a lot of data if you dont filter by database and table.
                IPS.avg_fragmentation_in_percent ,
                CAST(CASE WHEN IPS.avg_fragmentation_in_percent >= @rebuild_percentage
                          THEN ‘‘REBUILD‘‘
                          WHEN IPS.avg_fragmentation_in_percent >= @reorganization_percentage
                          THEN ‘‘REORGANIZE‘‘
                     END AS NVARCHAR(MAX)) AS index_operation
        FROM    sys.dm_db_index_physical_stats(NULL, NULL, NULL, NULL, NULL) IPS
                INNER JOIN sys.databases SD ON SD.database_id = IPS.database_id
                INNER JOIN sys.indexes SI ON SI.index_id = IPS.index_id
                INNER JOIN sys.objects SO ON SO.object_id = SI.object_id
                                             AND IPS.object_id = SO.object_id
                INNER JOIN sys.schemas SS ON SS.schema_id = SO.schema_id
        WHERE   alloc_unit_type_desc = ‘‘IN_ROW_DATA‘‘
                AND index_level = 0
                AND SD.name = ‘‘‘ + database_name + ‘‘‘
                AND IPS.avg_fragmentation_in_percent >= @reorganization_percentage
                AND SI.name IS NOT NULL -- Only review index, not heap data.
                AND SO.is_ms_shipped = 0 -- Do not perform maintenance on system objects
                ORDER BY SD.name ASC;‘
        FROM    @database_list
        WHERE   database_name IN ( SELECT    name    FROM    sys.databases );

        EXEC sp_executesql @sql_command, @parameter_list,
            @reorganization_percentage, @rebuild_percentage;

        SELECT  @sql_command = ‘‘;
        SELECT  @sql_command = @sql_command + ‘
            USE ‘ +  QUOTENAME(database_name) + ‘
            ALTER INDEX ‘ + QUOTENAME(index_name) + ‘ ON ‘ + QUOTENAME(schema_name) + ‘.‘ + QUOTENAME(object_name) + ‘ ‘
                + index_operation + ‘;‘
        FROM    #index_maintenance;

        SELECT  *
        FROM    #index_maintenance
        ORDER BY avg_fragmentation_in_percent DESC;

        IF @print_results_only = 1
        BEGIN
            PRINT @sql_command;
        END
        ELSE
        BEGIN
            EXEC sp_executesql @sql_command;
        END

        DROP TABLE #index_maintenance;
    END
GO

然后运行SP得到以下结果

EXEC dbo.index_maintenance_demo @reorganization_percentage = 10, @rebuild_percentage = 35,
@print_results_only = 1;

打印出来的SQL如下:

USE [AdventureWorks2014]
ALTER INDEX [PK_ProductCostHistory_ProductID_StartDate] ON [Production].[ProductCostHistory] REBUILD;
USE [AdventureWorks2014]
ALTER INDEX [AK_ProductDescription_rowguid] ON [Production].[ProductDescription] REBUILD;
USE [AdventureWorks2014]
ALTER INDEX [PK_DatabaseLog_DatabaseLogID] ON [dbo].[DatabaseLog] REBUILD;
USE [AdventureWorks2014]
ALTER INDEX [PK_ProductInventory_ProductID_LocationID] ON [Production].[ProductInventory] REBUILD;
USE [AdventureWorks2014]
ALTER INDEX [PK_ProductListPriceHistory_ProductID_StartDate] ON [Production].[ProductListPriceHistory] REBUILD;
USE [AdventureWorks2014]
ALTER INDEX [PK_SpecialOfferProduct_SpecialOfferID_ProductID] ON [Sales].[SpecialOfferProduct] REBUILD;
USE [AdventureWorks2014]
ALTER INDEX [AK_SpecialOfferProduct_rowguid] ON [Sales].[SpecialOfferProduct] REBUILD;
USE [AdventureWorks2014]
ALTER INDEX [PK_StateProvince_StateProvinceID] ON [Person].[StateProvince] REBUILD;
USE [AdventureWorks2014]
ALTER INDEX [PK_ProductModelProductDescriptionCulture_ProductModelID_ProductDescriptionID_CultureID] ON [Production].[ProductModelProductDescriptionCulture] REBUILD;
USE [AdventureWorks2014]
ALTER INDEX [AK_BillOfMaterials_ProductAssemblyID_ComponentID_StartDate] ON [Production].[BillOfMaterials] REORGANIZE;

有了这个SP,以后我们的维护工作就轻便了许多。

时间: 2024-12-19 20:40:16

T-SQL Recipes之Index Defragmentation的相关文章

Caused by: java.sql.SQLException: Parameter index out of range (1 > number of parameters, which is 0

1.错误描述 org.hibernate.exception.GenericJDBCException: error executing work at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(Sql

java.sql.SQLException: Parameter index out of range (0 < 1 )

由于初学写JDBC,所以烦了一个白痴错误,特此记录下来,以期望对寻求同样错误的小伙伴们一个答案,也是自己学习的一个总结记录 该异常的提示错误代码处并不是实际出现错误的代码处. 异常提示为:java.lang.RuntimeException: java.sql.SQLException: Parameter index out of range (0 < 1 ). 原文错误代码为: stmt = conn.prepareStatement("insert into users(name,p

Index Defragmentation

如果使用sys.dm_db_index_physical_stats 检查Index的 avg_fragmentation_in_percent 是一个非常大的数值,那么就要检查fragmentation是否影响到性能,如果导致性能下降,那么就需要 remove fragmentation. Fragmentation alone is not a sufficient reason to reorganize or rebuild an index. The main effect of fr

SQL Server -&gt;&gt; Online Index Rebuilding(联机索引重建)

SQL Server的Enterprise Edition是支持联机索引重建的.那么联机索引重建是怎么工作的以及对我们的查询有什么影响呢? 既然是联机,SQL Server保持了现有索引对于用户的可用,也就意味着它还不会去修改现有索引以及它相关联的统计数据.那么可以猜想下它的做法其实和SQL Server下Switch Partition就有相似的做法了 -- 最小化数据离线时间.它应该是先生成索引和统计数据,这个时候新的索引和统计数据肯定都分配好了object_id,内部肯定是已经可见了,只是

SQL Server 索引(index) 和 视图(view) 的简单介绍和操作

--索引(index)和视图(view)-- --索引(index)----概述: 数据库中的索引类似于书籍的目录,他以指针形式包含了表中一列或几列组合的新顺序,实现表中数据库的逻辑排序.索引创建在数据表或者视图的字段上,生成索引页,存放于数据库中.--分类:在SQL Server中,根据索引的作用和存储方式的不同,将索引分为聚集索引和非聚集索引两类. 1)聚集索引 :指数据库的物理顺序与被创建索引的字段顺序完全相同.同一个数据表最多只能创建一个聚集索引.默认情况下主键自动被创建为聚集索引.聚集

SQL大圣之路笔记——SQL 创建索引 index

1 --创建索引的标准:根据实际的sql语句进行分析,’列’出现在where中的次数较多,时间损耗较大,则为这个‘列’建立索引, 2 3 --注意:不是越多越好,因为没建立一个‘列’索引,都要排序存储一次,会让数据库文件变大 4 5 6 --创建索引 7 create index index_name 8 on 表名(列名)

SQL Server Rebuild Index

USE [msdb]GO /****** Object:  StoredProcedure [dbo].[IndexMaintain]    Script Date: 6/14/2013 1:46:17 PM ******/SET ANSI_NULLS ONGO SET QUOTED_IDENTIFIER ONGO CREATE  procedure  [dbo].[IndexMaintain]  as   SET NOCOUNT on  BEGIN TRY  declare @EXCEPTIO

SQL SERVER -&gt;&gt; Columnstore Index

谈到Columnstore index就不得不提SQL SERVER的压缩技术了.Columnstore就是用到了SQL SERVER的压缩技术.Columnstore又分Columnstore和Columnstore_Archive两种不同的压缩类型.Columnstore_Archive运用了Microsoft XPRESS压缩算法来实现比Columnstore更大的压缩比. 对于本身就是Columnstore的表,要么我们选择Columnstore,要么选择Columnstore_Arch

sql server中index的REBUILD和REORGANIZE

参考文献: http://technet.microsoft.com/en-us/library/ms188388.aspx 使用alter index来rebuild和reorganize索引来清除碎片,rebuild能够完全清除碎片,但是reorganize却不能. --1.准备实验数据 select * into Employee from AdventureWorks2008R2.HumanResources.Employee; --2.查看使用空间:Employee 290 72 KB