Riorganizzazione e ricostruzione degli indici

Il Motore di database di SQL Server esegue la manutenzione automatica degli indici dopo ogni operazione di modifica, inserimento o eliminazione dei dati sottostanti. Nel tempo, queste modifiche possono provocare la frammentazione dell'indice nel database. La frammentazione si verifica quando negli indici sono presenti pagine in cui l'ordinamento logico, basato sul valore chiave, non corrisponde all'ordinamento fisico all'interno del file di dati. Gli indici con un alto grado di frammentazione possono provocare una diminuzione delle prestazioni delle query e rallentare l'applicazione. Per ulteriori informazioni, vedere questo sito Web Microsoft.

È possibile porre rimedio alla frammentazione degli indici eseguendone una riorganizzazione o una ricostruzione. Per gli indici partizionati generati in base a uno schema di partizione è possibile procedere in uno dei modi seguenti sull'intero indice o su una singola partizione.

Rilevamento del grado di frammentazione

Il primo passaggio nel decidere il metodo di deframmentazione da utilizzare è eseguire un'analisi dell'indice per determinare il grado di frammentazione. La funzione di sistema sys.dm_db_index_physical_stats consente di rilevare la frammentazione in un indice specifico, in tutti gli indici di una tabella o vista indicizzata, in tutti gli indici di un database o in tutti gli indici di tutti i database. Per gli indici partizionati, sys.dm_db_index_physical_stats fornisce inoltre informazioni sulla frammentazione per ogni partizione.

Il set di risultati restituito dalla funzione sys.dm_db_index_physical_stats include le colonne seguenti.

Colonna

Descrizione

avg_fragmentation_in_percent

Percentuale di frammentazione logica (pagine non ordinate nell'indice).

fragment_count

Numero di frammenti (pagine foglia fisicamente consecutive) nell'indice.

avg_fragment_size_in_pages

Numero medio di pagine in un frammento di un indice.

Una volta noto il grado di frammentazione, utilizzare la tabella seguente per determinare il metodo migliore per la correzione della frammentazione.

Valore di avg_fragmentation_in_percent

Istruzione correttiva

> 5% e < = 30%

ALTER INDEX REORGANIZE

> 30%

ALTER INDEX REBUILD WITH (ONLINE = ON)*

* È possibile eseguire la ricostruzione di un indice in linea o non in linea. La riorganizzazione di un indice viene sempre eseguita in linea. Per ottenere una disponibilità simile a quella offerta dall'opzione di riorganizzazione è necessario ricostruire gli indici in modalità in linea.

Questi valori costituiscono un'indicazione approssimativa per determinare il punto in cui passare da ALTER INDEX REORGANIZE a ALTER INDEX REBUILD. I valori effettivi, in realtà, variano da caso a caso. È importante riuscire a determinare la soglia migliore per l'ambiente in uso.

Non è consigliabile utilizzare questi comandi per livelli ridotti di frammentazione (inferiori al 5%) poiché i vantaggi offerti dalla rimozione di una frammentazione così limitata sono praticamente annullati dal costo della riorganizzazione o della ricostruzione dell'indice.

[!NOTA]

In generale, non è possibile controllare la frammentazione sugli indici di dimensioni ridotte. Le pagine di indici di dimensioni ridotte vengono archiviate in extent misti. Poiché gli extent misti possono essere condivisi al massimo da otto oggetti, la frammentazione in un indice di dimensioni ridotte potrebbe non ridursi dopo la riorganizzazione o la ricostruzione dell'indice. Per ulteriori informazioni sugli extent misti, vedere Informazioni su pagine ed extent.

Esempio

Nell'esempio seguente viene eseguita una query sulla funzione a gestione dinamica sys.dm_db_index_physical_stats per ottenere la frammentazione media di tutti gli indici della tabella Production.Product. In base alla tabella precedente, la soluzione consigliata è riorganizzare PK_Product_ProductID e ricostruire gli altri indici.

USE AdventureWorks;
GO
SELECT a.index_id, name, avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats (DB_ID(), OBJECT_ID(N'Production.Product'),
     NULL, NULL, NULL) AS a
    JOIN sys.indexes AS b ON a.object_id = b.object_id AND a.index_id = b.index_id;
GO

L'istruzione può restituire un set di risultati simile al seguente.

index_id    name                        avg_fragmentation_in_percent
----------- --------------------------- ----------------------------
1           PK_Product_ProductID        15.076923076923077
2           AK_Product_ProductNumber    50.0
3           AK_Product_Name             66.666666666666657
4           AK_Product_rowguid          50.0

(4 row(s) affected)

Riorganizzazione di un indice

Per riorganizzare uno o più indici, utilizzare l'istruzione ALTER INDEX con la clausola REORGANIZE. Questa istruzione sostituisce l'istruzione DBCC INDEXDEFRAG. Per riorganizzare una singola partizione di un indice partizionato, utilizzare la clausola PARTITION di ALTER INDEX.

La riorganizzazione di un indice deframmenta il livello foglia degli indici cluster e non cluster delle tabelle e delle viste riordinando fisicamente le pagine al livello foglia, in modo che l'ordine corrisponda a quello logico (da sinistra a destra) dei nodi foglia. Il fatto che le pagine siano in ordine migliora le prestazioni delle operazioni di scansione degli indici. La riorganizzazione viene eseguita nell'ambito delle pagine esistenti allocate all'indice. Non vengono allocate nuove pagine. Se un indice si espande su più file, i file vengono riorganizzati uno alla volta. Non viene eseguita la migrazione delle pagine da un file all'altro.

La riorganizzazione, inoltre, compatta le pagine dell'indice. Le eventuali pagine vuote create in seguito alla compattazione vengono eliminate, liberando spazio su disco. La compattazione viene eseguita in base al valore del fattore di riempimento della vista del catalogo sys.indexes.

Il processo di riorganizzazione utilizza una quantità minima di risorse di sistema e viene eseguito automaticamente in modalità in linea. Il processo non applica blocchi di lunga durata, pertanto non blocca le query o gli aggiornamenti in esecuzione.

La riorganizzazione è da preferire quando l'indice non è molto frammentato. Le linee guida sulla frammentazione sono riportate nella tabella precedente. Se l'indice è molto frammentato si otterranno risultati migliori eseguendo una ricostruzione.

Compattazione di dati LOB (Large Object)

Oltre alla riorganizzazione di uno o più indici, i dati di tipo LOB (Large Object) contenuti nell'indice cluster o nella tabella sottostante vengono compattati per impostazione predefinita quando si esegue la riorganizzazione di un indice. I tipi di dati image, text, ntext, varchar(max), nvarchar(max), varbinary(max) e xml sono dati LOB. La compattazione di questi dati può migliorare l'utilizzo dello spazio su disco:

  • Riorganizzando un indice cluster specificato verrà eseguita la compattazione di tutte le colonne LOB contenute nel livello foglia (righe di dati) dell'indice.

  • Riorganizzando un indice non cluster verrà eseguita la compattazione di tutte le colonne LOB non chiave (incluse) contenute nell'indice.

  • Se si specifica ALL, tutti gli indici associati alla tabella o vista specificata vengono riorganizzati e tutte le colonne LOB associate all'indice cluster, alla tabella sottostante o all'indice non cluster con colonne incluse vengono compattate.

  • Se non sono presenti colonne LOB, la clausola LOB_COMPACTION viene ignorata.

Ricostruzione di un indice

Durante una ricostruzione, l'indice viene eliminato e ne viene creato uno nuovo. Così facendo, la frammentazione viene eliminata, viene recuperato spazio su disco compattando le pagine in base al fattore di riempimento specificato o esistente e le righe dell'indice vengono riordinate in pagine contigue (allocando nuove pagine se necessario). In questa situazione, le prestazioni del disco potrebbero migliorare grazie alla riduzione del numero di letture di pagina necessarie per ottenere i dati richiesti.

Per la ricostruzione degli indici cluster e non cluster è possibile utilizzare i metodi seguenti:

  • ALTER INDEX con la clausola REBUILD. Questa istruzione sostituisce l'istruzione DBCC DBREINDEX.

  • CREATE INDEX con la clausola DROP_EXISTING.

Ambedue i metodi eseguono la stessa funzione, ma è necessario considerarne vantaggi e svantaggi, in base a quanto illustrato nella tabella seguente.

Funzionalità

ALTER INDEX REBUILD

CREATE INDEX WITH DROP_EXISTING

È possibile modificare la definizione dell'indice aggiungendo o rimuovendo colonne chiave, modificando l'ordine delle colonne o modificando il tipo di ordinamento delle colonne.*

No

Sì**

È possibile impostare o modificare le opzioni per gli indici.

È possibile ricostruire più indici in un'unica transazione.

No

È possibile ricostruire la maggior parte degli indici in modalità in linea senza bloccare le query o gli aggiornamenti in esecuzione.

È possibile ripartizionare l'indice partizionato.

No

È possibile spostare l'indice in un altro filegroup.

No

È necessario ulteriore spazio temporaneo su disco.

La ricostruzione di un indice cluster provoca la ricostruzione di tutti gli indici non cluster associati.

No

A meno che non sia specificata la parola chiave ALL.

No

A meno che la definizione dell'indice non sia cambiata.

Gli indici che applicano i vincoli PRIMARY KEY e UNIQUE possono essere ricostruiti senza bisogno di eliminare e ricreare i vincoli.

È possibile la ricostruzione di un'unica partizione dell'indice.

No

* È possibile convertire un indice non cluster in un indice cluster specificando CLUSTERED nella definizione dell'indice. Questa operazione va eseguita con l'opzione ONLINE impostata su OFF. La conversione da indice cluster a indice non cluster non è supportata, indipendentemente dall'impostazione dell'opzione ONLINE.

** Se l'indice viene ricreato con lo stesso nome, le stesse colonne e lo stesso tipo di ordinamento, è possibile che l'operazione di ordinamento venga omessa. Durante l'operazione di ricostruzione viene verificato che le righe siano ordinate.

È inoltre possibile ricostruire un indice eliminandolo con l'istruzione DROP INDEX e quindi ricreandolo con un'istruzione CREATE INDEX separata. L'esecuzione di queste operazioni come istruzioni separate presenta numerosi svantaggi e non è consigliata.

Disattivazione di indici non cluster per ottimizzare l'utilizzo dello spazio su disco durante le operazioni di ricostruzione

Quando un indice non cluster viene disattivato, le relative righe di dati vengono eliminate, ma la definizione viene mantenuta nei metadati. Dopo la ricostruzione, l'indice viene attivato. Quando l'indice non cluster non è disattivato, l'operazione di ricostruzione richiede una quantità di spazio temporaneo su disco sufficiente a contenere il vecchio e il nuovo indice. Disattivando e ricostruendo un indice non cluster in transazioni separate, tuttavia, lo spazio su disco reso disponibile con la disattivazione dell'indice può essere riutilizzato dalla successiva operazione di ricostruzione o da qualunque altra operazione. Non è necessario ulteriore spazio, fatta eccezione per lo spazio su disco temporaneo richiesto dall'ordinamento, che in genere corrisponde al 20% della dimensione dell'indice. Se l'indice non cluster è sulla chiave primaria, qualsiasi vincolo attivo facente riferimento a FOREIGN KEY verrà automaticamente disattivato. Sarà necessario riattivare manualmente i vincoli dopo la ricostruzione dell'indice. Per ulteriori informazioni, vedere Disabilitazione di indici e Linee guida per l'abilitazione di indici e vincoli.

Ricostruzione di indici di grandi dimensioni

Gli indici con più di 128 extent vengono ricostruiti in due fasi separate: logica e fisica. Nella fase logica, le unità di allocazione esistenti utilizzate dall'indice vengono contrassegnate per la deallocazione, le righe di dati vengono copiate e ordinate, quindi spostate nelle nuove unità di allocazione create per archiviare l'indice ricostruito. Nella fase fisica, le unità di allocazione precedentemente contrassegnate per la deallocazione vengono fisicamente eliminate nelle transazioni brevi eseguite in background e che non richiedono molti blocchi. Per ulteriori informazioni, vedere Eliminazione e ricostruzione di oggetti di grandi dimensioni.

Impostazione delle opzioni per gli indici

Non è possibile specificare le opzioni per un indice quando se ne esegue la riorganizzazione. Quando si ricostruisce un indice utilizzando ALTER INDEX REBUILD o CREATE INDEX WITH DROP_EXISTING tuttavia, è possibile impostare le opzioni seguenti:

PAD_INDEX

DROP_EXISTING (solo CREATE INDEX)

FILLFACTOR

ONLINE

SORT_IN_TEMPDB

ALLOW_ROW_LOCKS

IGNORE_DUP_KEY

ALLOW_PAGE_LOCKS

STATISTICS_NORECOMPUTE

MAXDOP

[!NOTA]

Se non è necessaria un'operazione di ordinamento, o se l'ordinamento può essere eseguito in memoria, l'opzione SORT_IN_TEMPDB viene ignorata.

Inoltre, la clausola SET nell'istruzione ALTER INDEX consente di impostare le opzioni seguenti senza ricostruire l'indice:

ALLOW_PAGE_LOCKS

IGNORE_DUP_KEY

ALLOW_ROW_LOCKS

STATISTICS_NORECOMPUTE

Per ulteriori informazioni, vedere Impostazione delle opzioni di un indice.

Per ricostruire o riorganizzare un indice

ALTER INDEX (Transact-SQL)

Per ricostruire un indice eliminandolo e ricreandolo in un solo passaggio

CREATE INDEX (Transact-SQL)

Esempi

A. Ricostruzione di un indice

Nell'esempio seguente viene illustrata la ricostruzione di un singolo indice.

USE AdventureWorks;
GO
ALTER INDEX PK_Employee_EmployeeID ON HumanResources.Employee
REBUILD;
GO

B. Ricostruzione di tutti gli indici di una tabella e specifica delle opzioni

Nell'esempio seguente viene specificata la parola chiave ALL per ricostruire tutti gli indici associati alla tabella. Vengono specificate tre opzioni.

USE AdventureWorks;
GO
ALTER INDEX ALL ON Production.Product
REBUILD WITH (FILLFACTOR = 80, SORT_IN_TEMPDB = ON,
              STATISTICS_NORECOMPUTE = ON);
GO

C. Riorganizzazione di un indice con la compattazione dei dati LOB

Nell'esempio seguente viene riorganizzato un singolo indice cluster. Poiché l'indice contiene un tipo di dati LOB nel livello foglia, l'istruzione esegue anche la compattazione di tutte le pagine che contengono i dati LOB. Si noti che non è necessario specificare l'opzione WITH (LOB_Compaction) perché il valore predefinito è ON.

USE AdventureWorks;
GO
ALTER INDEX PK_ProductPhoto_ProductPhotoID ON Production.ProductPhoto
REORGANIZE ;
GO