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:
- Vytvoření zobrazení ukazující, které indexy byly fragmentovány, a procento fragmentovaných indexů.
- Vytvoření tabulky pro ukládání výsledků defragmentace indexu.
- Vytvoření uložené procedury pro analýzu a defragmentaci vybraného indexu.
- Vytvoření zobrazení pro zobrazení statistik výsledků defragmentace indexu.
- 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ů:
- 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á.
- 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ů.