sql >> Databáze >  >> RDS >> Sqlserver

Automatizace defragmentace indexu v databázi MS SQL Server

Předmluva

World Wide Web nabízí spoustu informací o defragmentaci indexu SQL Server nebo opětovném sestavení indexu SQL Server. Většina doporučení se však týká databází, které mají minimální dobu načítání (většinou v noci).

A co databáze, které se používají jak pro úpravu dat, tak pro získávání informací 24/7?

V tomto článku poskytnu mechanismus pro automatizaci defragmentace indexu SQL Server implementovaný v databázi používané ve společnosti, pro kterou pracuji. Tento mechanismus umožňuje pravidelnou defragmentaci požadovaných indexů, protože fragmentace indexů probíhá neustále v systému 24/7. K provedení defragmentace indexu jednou denně to často nestačí.

Řešení

Nejprve se podívejme na obecný přístup:

  1. Vytvoření zobrazení ukazující, které indexy byly fragmentovány, a procento fragmentovaných indexů.
  2. Vytvoření tabulky pro ukládání výsledků defragmentace indexu.
  3. Vytvoření uložené procedury pro analýzu a defragmentaci vybraného indexu.
  4. Vytvoření zobrazení pro zobrazení statistik výsledků defragmentace indexu.
  5. Vytvoření úlohy v Agentu pro spuštění implementované uložené procedury.

A nyní se podívejme na implementaci:

1. Vytvoření zobrazení ukazujícího, které indexy byly fragmentovány, a procento fragmentovaných indexů:

USE [Database_Name]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE view [srv].[vIndexDefrag]
as
with info as 
(SELECT
	[object_id],
	database_id,
	index_id,
	index_type_desc,
	index_level,
	fragment_count,
	avg_fragmentation_in_percent,
	avg_fragment_size_in_pages,
	page_count,
	record_count,
	ghost_record_count
	FROM sys.dm_db_index_physical_stats
    (DB_ID(N'Database_Name')
	, NULL, NULL, NULL ,
	N'DETAILED')
	where index_level = 0
	)
SELECT
	b.name as db,
	s.name as shema,
	t.name as tb,
	i.index_id as idx,
	i.database_id,
	idx.name as index_name,
	i.index_type_desc,i.index_level as [level],
	i.[object_id],
	i.fragment_count as frag_num,
	round(i.avg_fragmentation_in_percent,2) as frag,
	round(i.avg_fragment_size_in_pages,2) as frag_page,
	i.page_count as [page],
	i.record_count as rec,
	i.ghost_record_count as ghost,
	round(i.avg_fragmentation_in_percent*i.page_count,0) as func
FROM Info as i
inner join [sys].[databases]	as b	on i.database_id = b.database_id
inner join [sys].[all_objects]	as t	on i.object_id = t.object_id
inner join [sys].[schemas]	as s	on t.[schema_id] = s.[schema_id]
inner join [sys].[indexes]	as idx on t.object_id = idx.object_id and idx.index_id = i.index_id
 where i.avg_fragmentation_in_percent >= 30 and i.index_type_desc <> 'HEAP';
GO

Toto zobrazení zobrazuje pouze indexy s procentem fragmentace větším než 30, tj. indexy, které vyžadují defragmentaci. Zobrazuje pouze indexy, které nejsou haldami, protože ty druhé mohou vést k negativním účinkům, jako je zablokování takové haldy nebo další fragmentace indexu.

Pohled používá důležitý systémový pohled sys.dm_db_index_physical_stats.

2. Vytvoření tabulky pro ukládání výsledků defragmentace indexu:

USE [Database_Name]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [srv].[Defrag](
	[ID] [bigint] IDENTITY(794,1) NOT NULL,
	[db] [nvarchar](100) NULL,
	[shema] [nvarchar](100) NULL,
	[table] [nvarchar](100) NULL,
	[IndexName] [nvarchar](100) NULL,
	[frag_num] [int] NULL,
	[frag] [decimal](6, 2) NULL,
	[page] [int] NULL,
	[rec] [int] NULL,
        [func] [int] NULL,
	[ts] [datetime] NULL,
	[tf] [datetime] NULL,
	[frag_after] [decimal](6, 2) NULL,
	[object_id] [int] NULL,
	[idx] [int] NULL,
	[InsertUTCDate] [datetime] NOT NULL,
 CONSTRAINT [PK_Defrag] PRIMARY KEY CLUSTERED 
(
	[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY];
GO

ALTER TABLE [srv].[Defrag] ADD  CONSTRAINT [DF_Defrag_InsertUTCDate]  DEFAULT (getutcdate()) FOR [InsertUTCDate];
GO

Nejdůležitější na této tabulce je pamatovat na smazání dat (například dat, která jsou starší než 1 měsíc).

Pole tabulky budou srozumitelná od dalšího bodu.

3. Vytvoření uložené procedury pro analýzu a defragmentaci vybraného indexu:

USE [Database_Name]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [srv].[AutoDefragIndex]
AS
BEGIN
	SET NOCOUNT ON;

	--declaring required variables
	declare @IndexName nvarchar(100) --index name
	,@db nvarchar(100)			 --database name
	,@Shema nvarchar(100)			 --schema name
	,@Table nvarchar(100)			 --table name
	,@SQL_Str nvarchar (2000)		 --string for command generation
	,@frag decimal(6,2)				 --fragmentation percentage before defragmentation
	,@frag_after decimal(6,2)		 --fragmentation percentage after defragmentation
        --Number of fragments at the final level of the IN_ROW_DATA allocation unit
        ,@frag_num int				 
	,@func int					 --round(i.avg_fragmentation_in_percent*i.page_count,0)
	,@page int					 --number of index pages  
	,@rec int						 --total number of records
	,@ts datetime					 --date and time of defragmentation start
	,@tf datetime					 --date and time of defragmenation finish
	--Table or view object ID for which the index was created
        ,@object_id int					 
	,@idx int;						 --index ID

	--getting current date and time
	set @ts = getdate();
	
	--getting next index for defragmenation
	--Here the important index is selected. At that, a situation when one index is defragmented regularly, while other indexes are not selected for defragmentation is unlikely.
	select top 1
		@IndexName = index_name,
		@db=db,
		@Shema = shema,
		@Table = tb,
		@frag = frag,
		@frag_num = frag_num,
		@func=func,
		@page =[page],
		@rec = rec,
		@object_id = [object_id],
		@idx = idx 
	from  [srv].[vIndexDefrag]
	order by func*power((1.0-
	  convert(float,(select count(*) from SRV.[srv].[Defrag] vid where vid.db=db 
														 and vid.shema = shema
														 and vid.[table] = tb
														 and vid.IndexName = index_name))
	 /
	 convert(float,
                  case  when (exists (select top 1 1 from SRV.[srv].[Defrag] vid1 where vid1.db=db))
                            then (select count(*) from  SRV.[srv].[Defrag] vid1 where vid1.db=db)
                            else 1.0 end))
                    ,3) desc

	--if we get such index
	if(@db is not null)
	begin
	   --index reorganization
	   set @SQL_Str = 'alter index ['[email protected]+'] on ['[email protected]+'].['[email protected]+'] Reorganize';

		execute sp_executesql  @SQL_Str;

		--getting current date and time
		set @tf = getdate()

		--getting fragmentation percentage after defragmentation
		SELECT @frag_after = avg_fragmentation_in_percent
		FROM sys.dm_db_index_physical_stats
			(DB_ID(@db), @object_id, @idx, NULL ,
			N'DETAILED')
		where index_level = 0;

		--writing the result of work
		insert into SRV.srv.Defrag(
									[db],
									[shema],
									[table],
									[IndexName],
									[frag_num],
									[frag],
									[page],
									[rec],
									ts,
									tf,
									frag_after,
									object_id,
									idx
								  )
						select
									@db,
									@shema,
									@table,
									@IndexName,
									@frag_num,
									@frag,
									@page,
									@rec,
									@ts,
									@tf,
									@frag_after,
									@object_id,
									@idx;
		
		--upating statistics for index
		set @SQL_Str = 'UPDATE STATISTICS ['[email protected]+'].['[email protected]+'] ['[email protected]+']';

		execute sp_executesql  @SQL_Str;
	end
END

4. Vytvoření pohledu pro prohlížení statistik výsledků defragmentace indexu:

USE [Database_Name]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE view [srv].[vStatisticDefrag] as
SELECT top 1000
	  [db]
	  ,[shema]
          ,[table]
          ,[IndexName]
          ,avg([frag]) as AvgFrag
          ,avg([frag_after]) as AvgFragAfter
	  ,avg(page) as AvgPage
  FROM [srv].[Defrag]
  group by [db], [shema], [table], [IndexName]
  order by abs(avg([frag])-avg([frag_after])) desc;
GO

Toto zobrazení lze použít ke každodennímu informování správců o výsledcích automatizace defragmentace indexu.

5. Vytvoření úlohy v Agentu pro spuštění implementované uložené procedury

Zde musíme vybrat čas experimentálním způsobem. V mém případě jsem někde dostal 5 minut, někde – 1 hodinu.

Tento algoritmus lze rozšířit na několik databází, ale v tomto případě potřebujeme další bod 6:

Shromáždění všech statistik automatizace defragmentace indexu na jednom místě pro následné odeslání správcům.

A nyní bych se rád zastavil u již poskytnutých doporučení pro podporu indexů:

  1. Současná defragmentace všech indexů během minimálního zatížení databáze je pro systémy s nepřetržitým provozem nepřijatelná, protože indexy jsou neustále fragmentovány a není téměř žádný čas, kdy by databáze zůstala nečinná.
  2. Reorganizace indexu SQL Serveru – tato operace blokuje tabulku nebo oddíl (v případě rozděleného indexu), což není dobré pro systémy 24/7. Pak je přestavba indexu v režimu reálného času podporována pouze v řešení Enterprise a může také vést k poškození dat.

Tato metoda není optimální, ale dokáže si úspěšně poradit se zajištěním správné defragmentace indexů (nepřesahující 30–40 % fragmentace) pro jejich následné použití optimalizátorem pro plány provádění budov.

Budu vděčný za vaše komentáře s odůvodněnými klady a zápory tohoto přístupu, stejně jako za testované alternativní návrhy.

Odkazy

  • Reorganizovat a znovu vytvořit indexy
  • sys.dm_db_index_physical_stats

Užitečný nástroj:

dbForge Index Manager – praktický doplněk SSMS pro analýzu stavu indexů SQL a řešení problémů s fragmentací indexů.


  1. Jak seřadit podle dvou sloupců v SQL?

  2. SQL NOT IN nefunguje

  3. Softwarové společnosti, které pracují na Oracle D2k, PLSQL Technologies v Noidě

  4. Jak vytvořit tabulku se dvěma nebo více cizími klíči pomocí místnosti Android?