T-SQL Recipes之Index Defragmentation
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,以后我们的维护工作就轻便了许多。
以上是关于T-SQL Recipes之Index Defragmentation的主要内容,如果未能解决你的问题,请参考以下文章
T-SQL Recipes之Dynamic PIVOT and UNPIVOT
T-SQL Recipes之Customized Database Objects
T-SQL Recipes之 Table Variables and Temporary Tables and CTE